diff --git a/.github/scripts/count-repos.sh b/.github/scripts/count-repos.sh
index 469033df2f..a8495dd7d6 100644
--- a/.github/scripts/count-repos.sh
+++ b/.github/scripts/count-repos.sh
@@ -10,8 +10,8 @@ fi
#fusionauth-containers, fusionauth-theme-helper, etc
EXTRA_IN_JSON_NOT_NAMED_CORRECTLY=4
-# fusionauth-example-template and (temporarily) fusionauth-example-vue-sdk
-EXTRA_IN_GH_NOT_DISPLAYABLE=2
+# fusionauth-example-template and (temporarily) fusionauth-example-vue-sdk, fusionauth-quickstart-java-android-fusionauth-sdk
+EXTRA_IN_GH_NOT_DISPLAYABLE=3
cat astro/src/content/json/exampleapps.json|jq '.[]|.url' |sed 's/"//g'|sed 's!https://github.com/!!i' > json.list
COUNT_IN_JSON=`wc -l json.list |sed 's/^ *//' |sed 's/ .*//'`
diff --git a/.github/vale/styles/config/vocabularies/FusionAuth/accept.txt b/.github/vale/styles/config/vocabularies/FusionAuth/accept.txt
index 7f75e70213..0b285719a5 100644
--- a/.github/vale/styles/config/vocabularies/FusionAuth/accept.txt
+++ b/.github/vale/styles/config/vocabularies/FusionAuth/accept.txt
@@ -33,8 +33,7 @@ esport
failover
Fastly
FIDO
-FusionAuth
-fusionauth
+[Ff]usion[Aa]uth
gameplay
Guice
HAProxy
@@ -49,6 +48,7 @@ interoperate
Inversoft
ISP
Istio
+jinja
JMeter
JWT
Kaspersky
@@ -121,3 +121,4 @@ xkcd
Yubico
YubiKey
Zendesk
+Ctrl
\ No newline at end of file
diff --git a/DocsDevREADME.md b/DocsDevREADME.md
index ffc4cae981..62ffa2e462 100644
--- a/DocsDevREADME.md
+++ b/DocsDevREADME.md
@@ -124,7 +124,7 @@ You can find help for {props.topic} at [help](/help)
- you may need to coerce a prop into a boolean to use as a conditional for an expression. Such as `{!!props.message && {props.message}}`;
- JSON files are their own content collection in astro. You can reference these using the [JSON component](astro/src/components/JSON.astro)
- We have an alias mapped in [tsconfig](astro/tsconfig.json) that allows you to use absolute references from 'src'. Otherwise, imports must use relative paths.
-- If a doc pulls code from an example application, use the [RemoteContent](astro/src/components/RemoteContent.astro). You can also pull sections with tags: ``
+- If a doc pulls code from an example application, use the [RemoteContent](astro/src/components/RemoteContent.astro). You can also pull sections with tags: ``
### For API docs
- We have many APIs which return the same objects either singly (if called with an Id) or in an array (if called without an Id). If you are creating or modifying an API with this, see if you can use the -base pattern that the tenants and applications do to reduce duplicates.
@@ -133,7 +133,7 @@ You can find help for {props.topic} at [help](/help)
```
This field is required when theOtherField.enabled is set to true.
```
-- If a feature is only available when using a paid plan, use the [PremiumEditionBlurbApi](astro/src/content/docs/_shared/_premium-edition-blurb-api.astro) component `` fragment for API fields, and [PremiumEditionBlurb](astro/src/content/docs/_shared/_premium-edition-blurb.astro) component for any other location where the feature is mentioned in docs. Only mark the request API fields.
+- If a feature is only available when using a paid plan, use the [PremiumEditionBlurbApi](astro/src/content/docs/_shared/_premium-edition-blurb-api.astro) component `` fragment for API fields, and [PremiumEditionBlurb](astro/src/content/docs/_shared/_premium-edition-blurb.astro) component for any other location where the feature is mentioned in docs. Only mark the request API fields.
- If a feature is only available when using essentials, use the [AdvancedEditionBlurbApi](astro/src/content/docs/_shared/_advanced-edition-blurb-api.astro) component for API fields, and [AdvancedEditionBlurb](astro/src/content/docs/_shared/_advanced-edition-blurb.astro) for any other location where the feature is mentioned in docs. Only mark the request API fields with this.
- If a feature is only available when using enterprise, use the [EnterpriseEditionBlurbApi](astro/src/content/docs/_shared/_enterprise-edition-blurb-api.astro) component for API fields, and [EnterpriseEditionBlurb](astro/src/content/docs/_shared/_enterprise-edition-blurb.astro) for any other location where the feature is mentioned in docs. Only mark the request API fields with this.
- If you are working in the `/api/identity-providers` folder there is a `README` there to help you understand the structure and layout of the documentation for the Identity Providers API.
diff --git a/astro/public/img/blogs/passkeys-webinar/passkeys-webinar.png b/astro/public/img/blogs/passkeys-webinar/passkeys-webinar.png
new file mode 100644
index 0000000000..49b8c9d881
Binary files /dev/null and b/astro/public/img/blogs/passkeys-webinar/passkeys-webinar.png differ
diff --git a/astro/public/img/blogs/passkeys-webinar/webinar-alex.jpg b/astro/public/img/blogs/passkeys-webinar/webinar-alex.jpg
new file mode 100644
index 0000000000..5005cda9a5
Binary files /dev/null and b/astro/public/img/blogs/passkeys-webinar/webinar-alex.jpg differ
diff --git a/astro/public/img/blogs/quantum-computing/Gemini_Generated_Image.jpeg b/astro/public/img/blogs/quantum-computing/Gemini_Generated_Image.jpeg
new file mode 100644
index 0000000000..b74d7a5e24
Binary files /dev/null and b/astro/public/img/blogs/quantum-computing/Gemini_Generated_Image.jpeg differ
diff --git a/astro/public/img/blogs/quantum-computing/quantum.png b/astro/public/img/blogs/quantum-computing/quantum.png
new file mode 100644
index 0000000000..84557148e4
Binary files /dev/null and b/astro/public/img/blogs/quantum-computing/quantum.png differ
diff --git a/astro/public/img/blogs/understanding-mfa/understanding-mfa.png b/astro/public/img/blogs/understanding-mfa/understanding-mfa.png
new file mode 100644
index 0000000000..119a23c83e
Binary files /dev/null and b/astro/public/img/blogs/understanding-mfa/understanding-mfa.png differ
diff --git a/astro/public/img/docs/lifecycle/migrate-users/provider-specific/generic/authorised-redirect.png b/astro/public/img/docs/lifecycle/migrate-users/provider-specific/generic/authorised-redirect.png
new file mode 100644
index 0000000000..2aa2e92ecb
Binary files /dev/null and b/astro/public/img/docs/lifecycle/migrate-users/provider-specific/generic/authorised-redirect.png differ
diff --git a/astro/public/img/docs/lifecycle/migrate-users/provider-specific/generic/find-login-url.png b/astro/public/img/docs/lifecycle/migrate-users/provider-specific/generic/find-login-url.png
new file mode 100644
index 0000000000..fbf6158293
Binary files /dev/null and b/astro/public/img/docs/lifecycle/migrate-users/provider-specific/generic/find-login-url.png differ
diff --git a/astro/public/img/docs/lifecycle/migrate-users/provider-specific/generic/list-users.png b/astro/public/img/docs/lifecycle/migrate-users/provider-specific/generic/list-users.png
new file mode 100644
index 0000000000..6dc6a782e3
Binary files /dev/null and b/astro/public/img/docs/lifecycle/migrate-users/provider-specific/generic/list-users.png differ
diff --git a/astro/public/img/docs/lifecycle/migrate-users/provider-specific/wordpress/find-login-url.png b/astro/public/img/docs/lifecycle/migrate-users/provider-specific/wordpress/find-login-url.png
new file mode 100644
index 0000000000..d3fe461312
Binary files /dev/null and b/astro/public/img/docs/lifecycle/migrate-users/provider-specific/wordpress/find-login-url.png differ
diff --git a/astro/public/img/docs/lifecycle/migrate-users/provider-specific/wordpress/list-users.png b/astro/public/img/docs/lifecycle/migrate-users/provider-specific/wordpress/list-users.png
new file mode 100644
index 0000000000..4da9619674
Binary files /dev/null and b/astro/public/img/docs/lifecycle/migrate-users/provider-specific/wordpress/list-users.png differ
diff --git a/astro/src/components/Footer.astro b/astro/src/components/Footer.astro
index ff9f37dedd..23dbb8e1cb 100644
--- a/astro/src/components/Footer.astro
+++ b/astro/src/components/Footer.astro
@@ -5,7 +5,7 @@ const { sections = [
{
"heading": "Get Started",
"items": [
- { "label": "download locally", "href": "/docs/v1/tech/installation-guide/docker" },
+ { "label": "download locally", "href": "/docs/get-started/download-and-install/docker/" },
{ "label": "run in cloud", "href": "/pricing?step=plan&hosting=basic-cloud" },
{ "label": "quickstarts", "href": "/docs/quickstarts/" },
]
@@ -20,10 +20,10 @@ const { sections = [
{
"heading": "developers",
"items": [
- { "label": "SDKs", "href": "/docs/v1/tech/client-libraries/" },
- { "label": "API Docs", "href": "/docs/v1/tech/apis/" },
- { "label": "Documentation", "href": "https://fusionauth.io/docs/v1/tech/" },
- { "label": "release notes", "href": "/docs/v1/tech/release-notes" },
+ { "label": "SDKs", "href": "/docs/sdks/" },
+ { "label": "API Docs", "href": "/docs/apis/" },
+ { "label": "Documentation", "href": "/docs/" },
+ { "label": "release notes", "href": "/docs/release-notes/" },
]
},
{
diff --git a/astro/src/components/JSON.astro b/astro/src/components/JSON.astro
index 92898e0019..c4627edd90 100644
--- a/astro/src/components/JSON.astro
+++ b/astro/src/components/JSON.astro
@@ -21,7 +21,7 @@ const parsedTitle = title ? parseInline(title) : '';
const code = JSON.stringify(json.data, null, 2);
---
{/* title is optional, do not render an empty em tag */}
-{title && }
+{title &&
- The password that FusionAuth will use to connect to the database. If you are using [Silent Mode](/docs/get-started/download-and-install/silent-mode) or [Setup Wizard](/docs/get-started/run-in-the-cloud/setup-wizard), then this password might be used when the database.user is automatically created for you, depending on your database server and configuration options.
+ The password that FusionAuth will use to connect to the database. If you are using Silent Mode or Setup Wizard, then this password might be used when the database.user is automatically created for you, depending on your database server and configuration options.
@@ -69,7 +69,7 @@ Display configuration values in format:
- The password that FusionAuth will use to connect to the database server as part of [Silent Mode](/docs/get-started/download-and-install/silent-mode) or [Setup Wizard](/docs/get-started/run-in-the-cloud/setup-wizard) if it cannot connect using the database.username and database.password values. This should be the password for a user that has superuser privileges to the database. For example, many PostgreSQL servers will provide you with a password for the postgres user. Similarly, MySQL servers sometimes leave the password blank for the mysql user.
+ The password that FusionAuth will use to connect to the database server as part of Silent Mode or Setup Wizard if it cannot connect using the database.username and database.password values. This should be the password for a user that has superuser privileges to the database. For example, many PostgreSQL servers will provide you with a password for the postgres user. Similarly, MySQL servers sometimes leave the password blank for the mysql user.
@@ -79,7 +79,7 @@ Display configuration values in format:
- The password that FusionAuth will use to connect to the database server as part of [Silent Mode](/docs/get-started/download-and-install/silent-mode) or [Setup Wizard](/docs/get-started/run-in-the-cloud/setup-wizard) if it cannot connect using the database.username and database.password values. This should be the username that has superuser privileges to the database. For example, many PostgreSQL servers use the username of postgres for the superuser account. Similarly, MySQL servers often use the username mysql for the superuser account.
+ The password that FusionAuth will use to connect to the database server as part of Silent Mode or Setup Wizard if it cannot connect using the database.username and database.password values. This should be the username that has superuser privileges to the database. For example, many PostgreSQL servers use the username of postgres for the superuser account. Similarly, MySQL servers often use the username mysql for the superuser account.
@@ -142,7 +142,7 @@ Display configuration values in format:
- The database user that FusionAuth will use to connect to the database. If you are using [Silent Mode](/docs/get-started/download-and-install/silent-mode) or [Setup Wizard](/docs/get-started/run-in-the-cloud/setup-wizard), then this user might be automatically created for you, depending on your database server and configuration options. If this user is created as part of [Silent Mode](/docs/get-started/download-and-install/silent-mode) or [Setup Wizard](/docs/get-started/run-in-the-cloud/setup-wizard), it will also be the owner of the database and schema for FusionAuth.
+ The database user that FusionAuth will use to connect to the database. If you are using Silent Mode or Setup Wizard, then this user might be automatically created for you, depending on your database server and configuration options. If this user is created as part of Silent Mode or Setup Wizard, it will also be the owner of the database and schema for FusionAuth.
Deprecated names:
@@ -191,6 +191,10 @@ Display configuration values in format:
Deprecated in version 1.37.0.
+
+ The name of the Elasticsearch index that will be created by FusionAuth to index entities.
+
+
The port number that FusionAuth will use to make HTTP requests to itself over localhost. This is port is only used by FusionAuth itself, when making an API call, or using any browser based connections to FusionAuth, please use the port configured by fusionauth-app.http.port.
@@ -357,15 +361,19 @@ Display configuration values in format:
production
- When in the development runtime mode, maintenance mode will interactively assist you configuring the database and connecting to Elasticsearch if configured. Once you move FusionAuth into production, it is recommended that you modify the runtime mode to production. When in production runtime mode maintenance mode will no longer be available to you which means you can be certain that your end users will not find themselves on the database upgrade panel during an Upgrade. When in production mode you will either need to leverage [Silent Mode](/docs/get-started/download-and-install/silent-mode) to automatically apply database migrations or you will need to apply the database migrations yourself (either by hand or via a script of some sort).
+ When in the development runtime mode, maintenance mode will interactively assist you configuring the database and connecting to Elasticsearch if configured. Once you move FusionAuth into production, it is recommended that you modify the runtime mode to production. When in production runtime mode maintenance mode will no longer be available to you which means you can be certain that your end users will not find themselves on the database upgrade panel during an Upgrade. When in production mode you will either need to leverage Silent Mode to automatically apply database migrations or you will need to apply the database migrations yourself (either by hand or via a script of some sort).
Deprecated names:
fusionauth.runtime-mode
+
+ The default search index refresh_interval. Bulk operations (bulk user import and reindex) may temporarily relax this value for better performance and then set it back to this value. This value specifies the delay before updated User and Entity data are available to search requests. Must be a valid Elasticsearch Date/Time Interval Value, however setting this variable is not recommended.
+
+
- Determines if FusionAuth should use [Silent Mode] during the startup process. Previous to version 1.19.0, Silent Mode was only available when the [field](/docs/get-started/download-and-install/silent-mode)#fusionauth-app.runtime-mode# was development. This has been changed so that FusionAuth can now automatically apply database migrations during the startup process.
+ Determines if FusionAuth should use [Silent Mode] during the startup process. Previous to version 1.19.0, Silent Mode was only available when the fusionauth-app.runtime-mode was development. This has been changed so that FusionAuth can now automatically apply database migrations during the startup process.
diff --git a/astro/src/components/quickstarts/QuickstartTshirtCTA.astro b/astro/src/components/quickstarts/QuickstartTshirtCTA.astro
new file mode 100644
index 0000000000..e8b872c969
--- /dev/null
+++ b/astro/src/components/quickstarts/QuickstartTshirtCTA.astro
@@ -0,0 +1,42 @@
+
+
+
+ Made it this far? Get a free t-shirt, on
+ us!
+
+
+ Thank you for spending some time getting familiar with FusionAuth.
+
+ *Offer only valid in the United States and Canada, while supplies last.
+
+
+
+
+
+
+
diff --git a/astro/src/content/articles/authentication/passwordless-authentication-security.mdx b/astro/src/content/articles/authentication/passwordless-authentication-security.mdx
index 9a7bd3f4c9..c4bdc57883 100644
--- a/astro/src/content/articles/authentication/passwordless-authentication-security.mdx
+++ b/astro/src/content/articles/authentication/passwordless-authentication-security.mdx
@@ -1,14 +1,14 @@
---
title: The Security Implications Of Passwordless Authentication
-description: Learn about passwordless authentication.
+description: Learn about passkeys and other passwordless authentication.
author: Niel Thiart
section: Authentication
-tags: passwords, security, passwordless, biometrics, webauthn
+tags: passwords, security, passwordless, passkeys, webauthn
icon: /img/icons/passwordless-authentication-image.svg
darkIcon: /img/icons/passwordless-authentication-image-dark.svg
---
-With the rise of passkeys and biometrics in laptops and phones, passwordless authentication seems within reach. Here's what you need to consider.
+With the rise of passkeys accessed by biometrics in laptops and phones, passwordless authentication seems within reach. Here's what you need to consider.
Tech luminaries have been prophesizing the imminent death of the password for the past 20 years. Or so I've been led to believe. If you Google the phrase "the password is dead", you'll find an endless list of articles pointing to the 2004 RSA Conference, where Bill Gates allegedly predicted the password's sudden demise. A death that, as Mark Twain would have put it, has been greatly exaggerated.
@@ -30,11 +30,11 @@ The casual way in which Apple, Google, and Microsoft present this technology to
Let's unpack the nebulous concept of passwordless authentication.
![Security implications of passwordless](/img/articles/passwordless-authentication-security/security-implications-of-passwordless.png)
-## Why Now: The Rise Of Biometrics
+## Why Now: The Rise Of Passkeys
In 2013, Apple announced Touch ID for the iPhone 5s. Users could now unlock their phones by pressing a finger against a tiny sensor on the front of the phone. In subsequent models, Apple expanded Touch ID capabilities to authenticate App Store purchases and make electronic payments. Apple later added Face ID, which allowed users to do anything Touch ID could do, but by just looking at their phone's screen.
-Other device manufacturers released similar features, to the point where almost all new mobile devices released today feature some form of biometric authentication. Initially, some users raised concerns about sharing biometric data with service providers, and the potential for abuse by powerful state actors. At least in the case of Apple, face and fingerprint data never leave the user's device unencrypted, and are isolated in a secure enclave while saved on the device.
+Other device manufacturers released similar features, to the point where almost all new mobile devices released today feature some form of biometric authentication for passkey authorization. Initially, some users raised concerns about sharing biometric data with service providers, and the potential for abuse by powerful state actors. At least in the case of Apple, face and fingerprint data never leave the user's device unencrypted, and are isolated in a secure enclave while saved on the device.
Biometric authentication proves with a great degree of certainty that the user is present, and these methods are difficult to spoof. Most importantly, unlocking your phone by looking at a screen is as low friction as it gets. This convenience is a critical part of passwordless authentication's success.
diff --git a/astro/src/content/articles/authentication/webauthn.mdx b/astro/src/content/articles/authentication/webauthn.mdx
index 0383f18d72..18de891db1 100644
--- a/astro/src/content/articles/authentication/webauthn.mdx
+++ b/astro/src/content/articles/authentication/webauthn.mdx
@@ -1,9 +1,9 @@
---
title: Decoding How WebAuthn Works
-description: A WebAuthn deep dive.
+description: A deep dive into the standard of WebAuthn, and how passkeys are bringing passwordless authentication to the masses.
author: Niel Thiart
section: Authentication
-tags: webauthn, passwords, security, passwordless, biometrics
+tags: webauthn, passwords, security, passwordless, biometrics, passkeys
icon: /img/icons/webauthn-explained.svg
darkIcon: /img/icons/webauthn-explained-dark.svg
---
@@ -11,9 +11,17 @@ import WebAuthnComponentsChart from "../../../diagrams/articles/authentication/w
import WebAuthnRegistrationFlow from "../../../diagrams/articles/authentication/webauthn/webauthn-registration.astro";
import WebAuthnAuthenticationFlow from "../../../diagrams/articles/authentication/webauthn/webauthn-authentication.astro";
-Think passwordless authentication seems too good to be true? Take a look under the hood to see how and why WebAuthn works.
+Think passwordless authentication seems too good to be true? Take a look under the hood to see how and why passkeys work.
-My first brush with [WebAuthn](/articles/authentication/webauthn-explained) came as an end user. From the user's perspective, WebAuthn is referred to as passkeys. Here's how it went:
+Before we jump into the deep end, it's worth taking some time to define the terms that we'll use in this article:
+
+[WebAuthn](/docs/apis/webauthn)- WebAuthn is a web standard, the goal of which is to standardize an interface for user authentication to web-based applications. It was published by the World Wide Web Consortium (W3C), as one of the core components of the [FIDO2 Project](/blog/authenticators-ceremonies-webauthn-oh-my).
+
+[Passkeys](/guides/what-is-a-passkey) - A passkey is a FIDO credential, stored in two parts, used to log in to a web service. The private key exists on a computer or phone or physical security key. The public key lives on a web server.
+
+The security world seems to have landed on using "passkeys" as a blanket term to describe biometric logins using the WebAuthn standard. With that in mind, we'll use the word passkeys liberally in this article.
+
+With all that being said, now we can dive in. My first brush with passkeys came as an end user. Here's how it went:
I added passkeys to a handful of online services — while registration had some usability issues, the authentication experience has been nothing short of sublime. Tapping the Touch ID button on my Mac to log in to GitHub, for example, is as simple as it gets. No dongles, no need to enter a username, no backup codes, no authentication apps on my phone, and, most importantly, no password.
@@ -21,18 +29,18 @@ If anything seems too good to be true, it probably is. The first issue came up w
In this case, the solution was easy: I logged in to GitHub on Android using my trusty old password and generated a new passkey, this time using my Google account's password manager. Surely, this would allow me to log in using my Google profile in Chrome on Linux too. Turns out that didn't work.
-I've since found a solution to cross-platform passwordless authentication in the form of a password manager, but I'll discuss that later on in strategies to deal with WebAuthn as a user.
+I've since found a solution to cross-platform passwordless authentication in the form of a password manager, but I'll discuss that later on in strategies to deal with passkeys as a user.
-This speed bump prompted questions. What if my Android device is lost or stolen? Could this marvel of convenience be compromised? How do I ensure I remember which device I created a passkey on? And my burning question was: **How does WebAuthn even work?**
+This speed bump prompted questions. What if my Android device is lost or stolen? Could this marvel of convenience be compromised? How do I ensure I remember which device I created a passkey on? And my burning question was: **How do passkeys even work?**
-Complacency and digital security don't mix. I set out to understand the nuances of WebAuthn with two goals: as a responsible consumer, assess the true cost of this convenience, and as a developer, is WebAuthn something I should add to my product? Ignorance, in this context, isn't bliss — it's a liability.
+Complacency and digital security don't mix. I set out to understand the nuances of passkeys with two goals: as a responsible consumer, assess the true cost of this convenience, and as a developer, are passkeys something I should add to my product? Ignorance, in this context, isn't bliss — it's a liability.
Here's what I found.
[![Download passkeys whitepaper](/img/cta/passkeys-future.png)](/tech-papers/why-passkeys-improve-user-security-how-to-implement-them)
-## Why we need WebAuthn
+## Why we need Passkeys
-WebAuthn is an attempt to make strong authentication more convenient than passwords.
+Passkeys are an attempt to make strong authentication more convenient than passwords.
### The problem with passwords
@@ -50,13 +58,13 @@ While [Multifactor Authentication](/articles/authentication/multi-factor-authent
Cyberattacks are now a daily drama on a global scale, with compromised credentials as the opening act. Stolen passwords give hackers the keys they need to set off a cascade of often terrible events. It's not just big corporations at risk. Small businesses, [children's hospitals](https://www.bleepingcomputer.com/news/security/ransomware-gang-apologizes-gives-sickkids-hospital-free-decryptor/), and even [small towns](https://www.nytimes.com/2019/08/20/us/texas-ransomware.html) have all fallen victim lately.
-### WebAuthn can help us here
+### Passkeys can help us here
-WebAuthn aims to decrease our reliance on passwords, streamline authentication processes, and sharply reduce opportunities for phishing and breaches. By integrating biometrics and hardware tokens, WebAuthn can make logging in both more secure and more convenient — potentially even rendering traditional passwords and MFA obsolete.
+Passkeys aim to decrease our reliance on passwords, streamline authentication processes, and sharply reduce opportunities for phishing and breaches. By integrating biometrics and hardware tokens, passkeys can make logging in both more secure and more convenient — potentially even rendering traditional passwords and MFA obsolete.
## What is WebAuthn?
-[WebAuthn](/articles/authentication/webauthn-explained), short for Web Authentication, is a web standard that enables online users to perform authentication through cryptographic proof, using credentials generated by their devices. This authentication process doesn't require a password and instead verifies the user through biometrics.
+[WebAuthn](/articles/authentication/webauthn-explained), short for Web Authentication, is a web standard that enables online users to perform authentication through cryptographic proof, using credentials generated by their devices. This authentication process doesn't require a password and instead verifies the user through biometrics or a fallback PIN.
### The FIDO Alliance
@@ -70,35 +78,35 @@ Changes to the WebAuthn specification happen through an established process that
### How browsers implement the API
-Just as with HTML and CSS, each web browser may choose to support WebAuthn features at its own pace, but there's a strong incentive to stay current: users and developers demand the balanced diet of convenience and security afforded by WebAuthn. Modern browsers are typically quick to implement updates, to make sure users get the latest while the specification evolves.
+Just as with HTML and CSS, each web browser may choose to support WebAuthn features at its own pace, but there's a strong incentive to stay current: users and developers demand the balanced diet of convenience and security afforded by passkeys. Modern browsers are typically quick to implement updates, to make sure users get the latest while the specification evolves.
![Screenshot of Caniuse showing WebAuthn support at 96.36%](/img/articles/webauthn/webauthn-caniuse.png)
-As of early 2024, WebAuthn is supported by browsers for 96.36% of all users.
+As of early 2024, passkeys are supported by browsers for 96.36% of all users.
-## The core components of WebAuthn and FIDO2
+## The core components of WebAuthn, Passkeys, and FIDO2
-WebAuthn is built upon three core components that work together to enable passwordless authentication:
+Passkeys are built upon three core components that work together to enable passwordless authentication:
- **Authenticators**: Devices or software like *phones, security keys, or password managers* that generate and store cryptographic credentials securely.
-- **Relying Parties**: Services or applications like *online banking or social media platforms* that verify authentication data provided by the authenticator. Called the relying party because it relies on the WebAuthn process to confirm that the user is who they claim to be.
+- **Relying Parties**: Services or applications like *online banking or social media platforms* that verify authentication data provided by the authenticator. Called the relying party because it relies on the passkey process to confirm that the user is who they claim to be.
- **Clients**: Browsers and operating systems such as *Google Chrome or macOS* that facilitate communication between authenticators and relying parties.
### Public-key cryptography
-Public-key cryptography is the base on which WebAuthn is built. A detailed overview of public key cryptography is beyond the scope of this article, but rest assured that it is a solid, well-tested, and widely used technology. It's the same technology that secures HTTPS connections, for example.
+Public-key cryptography is the base on which WebAuthn and passkeys are built. A detailed overview of public key cryptography is beyond the scope of this article, but rest assured that it is a solid, well-tested, and widely used technology. It's the same technology that secures HTTPS connections, for example.
-There are two kinds of cryptographic keys involved: the **private key** and the **public key**. They do exactly what their names suggest: the private key is kept secret, while the public key is shared with the world.
+There are two kinds of cryptographic keys involved: the **private key** and the **public key**. They do exactly what their names suggest: the private key is kept secret, while the public key is openly accessible.
-In the context of WebAuthn, the private key is stored on the authenticator, while the public key is shared with the relying party. The authenticator uses the private key to generate a digital signature that proves the user's identity to the relying party, without revealing the private key itself.
+In the context of passkeys, the private key is stored on the authenticator, while the public key is shared with the relying party. The authenticator uses the private key to generate a digital signature that proves the user's identity to the relying party, without revealing the private key itself.
Even if malicious actors get their hands on your public key, without the corresponding private key, it is essentially harmless and pointless for them.
### The role of web browsers
-Web browsers act as the facilitators for the WebAuthn process, but they don't store any of the user's credentials. Instead, they act as the middleman between the authenticator and the relying party. The browser's role is to pass messages between the two and to ensure that the user is aware of what's happening.
+Web browsers act as the facilitators for the passkey authentication process, but they don't store any of the user's credentials. Instead, they act as the middleman between the authenticator and the relying party. The browser's role is to pass messages between the two and to ensure that the user is aware of what's happening.
-## The processes of WebAuthn
+## The processes of Passkeys
The WebAuthn specification distinguishes two main processes: Registration and Authentication. While it might seem a bit dramatic to call them "ceremonies," that's the term the standard uses, and it helps underline the formality and importance of the steps involved. Registration is where a new credential is created and tied to a particular user and device, while Authentication is how that credential is used to confirm the user's identity on subsequent visits.
@@ -110,7 +118,7 @@ The WebAuthn registration ceremony is the initial step where your device creates
#### Registration step one: Visiting a website
-We start by visiting a website that supports WebAuthn. Let's say we're registering on `https://auth.example.com:8080`. At this point, the website is called the **relying party**. The relying party is the website that wants to authenticate you, and it's the website that will store your public key and attestation. You may already be logged in to the website, or you may be asked to log in using a password or other means.
+We start by visiting a website that supports passkeys. Let's say we're registering on `https://auth.example.com:8080`. At this point, the website is called the **relying party**. The relying party is the website that wants to authenticate you, and it's the website that will store your public key and attestation. You may already be logged in to the website, or you may be asked to log in using a password or other means.
#### Registration step two: Initiating the registration ceremony
@@ -132,7 +140,7 @@ Parameters passed to the `navigator.credentials.create()` method include the fol
#### Registration step two: Select an authenticator
-Only if you have multiple authenticators connected to your device, you'll be asked to select which one you want to use. For example, you may have a YubiKey, a security key, and a password manager installed on your device.
+If you have multiple authenticators (say a YubiKey, a security key, and a password manager) connected to your device, you'll be asked to select which one you want to use.
#### Registration step three: User consent
@@ -142,7 +150,7 @@ Your browser will pass the parameters to the authenticator, which will then ask
The authenticator will now ask you to verify your identity. This is typically done through biometrics, such as Touch ID or Face ID.
-#### Registration step five: Create a new credential
+#### Registration step five: Create a new credential
If you're successfully verified, the authenticator will generate a new set of keys for this website. The private key will be stored on the authenticator, while the public key will be shared with the relying party.
@@ -206,7 +214,7 @@ Here is a simplified visualization of the WebAuthn authentication ceremony:
-## Why WebAuthn is secure
+## Why passkeys are secure
Now that we know *how* it works, let's discuss why this is secure.
@@ -220,13 +228,13 @@ It also means that even if someone steals your device, they won't be able to log
### Asymmetric authentication limits the role of relying parties
-In WebAuthn, "relying parties" are the websites or services that use WebAuthn for user authentication. They rely on the protocol to confirm users' identities without ever seeing their private keys, which remain securely stored on users' devices.
+In WebAuthn, "relying parties" are the websites or services that use WebAuthn for user authentication. They rely on the protocol to confirm users' identities without ever seeing their private keys, which remain securely stored on users' devices.
This system limits their role in the authentication process, enhancing security and reducing the risk of credential exposure during data breaches.
-### WebAuthn credentials are strong, attested, and scoped
+### Passkey credentials are strong, attested, and scoped
-WebAuthn credentials offer three significant security features: attestation, a unique credential ID, and scoped authentication.
+Passkey credentials offer three significant security features: attestation, a unique credential ID, and scoped authentication.
Attestation acts as a certificate of legitimacy for a public key, allowing a relying party to verify that the public key comes from a genuine source — the user's authenticator — without ever seeing the private key.
@@ -234,33 +242,33 @@ Each WebAuthn credential is also bound to a unique identifier, making it nearly
Lastly, credential scoping ensures that the authentication process is tightly bound to the entity initiating it. To put it simply, credentials created for one website can't be used on a different website.
-### How WebAuthn prevents phishing and limits damage from data breaches
+### How passkeys prevent phishing and limits damage from data breaches
Because the credentials are unique to each website, users can't be tricked into using them on a phishing site. Even if a user is fooled into entering their credentials on a fake site, the credentials won't work on the real site.
-## WebAuthn in practice
+## Passkeys in practice
-Let's take a look at how WebAuthn is used in the real world today.
+Let's take a look at how passkeys are used in the real world today.
-### WebAuthn at Microsoft
+### Passkeys at Microsoft
-WebAuthn has been embraced by major players in the tech industry due to its promise of a more secure, passwordless future. Microsoft, for instance, has integrated WebAuthn-based authentication across its services. Users of Windows 11 and above can use the operating system's native features, such as Windows Hello, to authenticate themselves using facial recognition, a fingerprint, or a PIN instead of traditional passwords.
+Passkeys have been embraced by major players in the tech industry due to its promise of a more secure, passwordless future. Microsoft, for instance, has integrated passkey authentication across its services. Users of Windows 11 and above can use the operating system's native features, such as Windows Hello, to authenticate themselves using facial recognition, a fingerprint, or a PIN instead of traditional passwords.
-Microsoft Entra ID (formerly Azure Active Directory), for example, already offers passwordless authentication through WebAuthn.
+Microsoft Entra ID (formerly Azure Active Directory), for example, already offers passwordless authentication through passkeys.
Moreover, the tech giant has enabled WebAuthn support for its browser, Microsoft Edge.
-### How to enable your employees to use WebAuthn
+### How to enable your employees to use Passkeys
-Equipping your employees with WebAuthn-enabled devices is a great way to ensure that they can use WebAuthn to log in to your company's services. This is especially true if you're using a password manager that supports WebAuthn, such as 1Password.
+Equipping your employees with passkey-enabled devices is a great way to ensure that they can use passkeys to log in to your company's services. This is especially true if you're using a password manager that supports passkeys, such as 1Password.
-An alternative strategy is to provide your employees with security keys, such as the YubiKey. These devices can be used to store WebAuthn credentials and can be used to log in to any service that supports WebAuthn.
+An alternative strategy is to provide your employees with security keys, such as the YubiKey. These devices can be used to store their credentials and can be used to log in to any service that supports passkeys.
-Regardless of which strategy you choose, it's important to educate your employees about the benefits and especially the risks of WebAuthn.
+Regardless of which strategy you choose, it's important to educate your employees about the benefits and especially the risks of passkeys.
-### How WebAuthn can benefit web applications and platforms today
+### How passkeys can benefit web applications and platforms today
-WebAuthn has already found traction in many web applications, offering improved security profiles and a smoother user experience.
+Passkeys have already found traction in many web applications, offering improved security profiles and a smoother user experience.
I've added passkeys to my GitHub account, for example, and I'm using them to log in to GitHub on my Mac. I've also added passkeys to my Google account, and I'm using them to log in to Google services on my Android phone.
@@ -268,7 +276,7 @@ These services benefit from the improved security profile of WebAuthn, and I ben
### The challenges we'll face
-As the adoption of WebAuthn grows, so too will the challenges encountered by both users and organizations.
+As the adoption of passkeys grows, so too will the challenges encountered by both users and organizations.
A critical issue lurking behind the newfound convenience is the heavy reliance on specific devices: a smartphone, tablet, or security key holds the keys to one's digital life. If devices are lost, damaged, or replaced without a proper backup strategy, users could find themselves locked out of their accounts.
@@ -278,20 +286,20 @@ The reliance on biometrics is another potential pitfall. While biometric authent
### What lies ahead
-It remains to be seen whether WebAuthn will gain widespread adoption and reach critical mass. Users tend to use the path of least resistance, and WebAuthn's registration flow is still a bit clunky. The authentication flow, however, is a joy to use.
+It remains to be seen whether passkeys will gain widespread adoption and reach critical mass. Users tend to use the path of least resistance, and WebAuthn's registration flow is still a bit clunky. The authentication flow, however, is a joy to use.
-Either way, I don't think WebAuthn will replace passwords anytime soon. Instead, I think it will be used in conjunction with passwords, and other authentication methods, such as MFA.
+Either way, I don't think passkeys will replace passwords anytime soon. Instead, I think it will be used in conjunction with passwords, and other authentication methods, such as MFA.
-When used alongside passwords, I'm doubtful that WebAuthn will have a significant impact on the damage caused by data breaches. If a hacker can steal your password, they might not need your WebAuthn credentials to log in to your account.
+When used alongside passwords, I'm doubtful that WebAuthn will have a significant impact on the damage caused by data breaches. If a hacker can steal your password, they might not need your passkey credentials to log in to your account.
Users might also still be tricked into using phishing sites with their passwords. If they do, the hacker might be able to use the stolen password to log in to real accounts.
## Conclusion
-With the usability and education issues ironed out, WebAuthn has the potential to greatly improve online security for users and organizations alike. It's a promising step towards a passwordless future, but it's not a silver bullet. For now, I'll treat it as a welcome addition to my security arsenal, but I'll keep my password manager close at hand.
+With the usability and education issues ironed out, passkeys have the potential to greatly improve online security for users and organizations alike. It's a promising step towards a passwordless future, but it's not a silver bullet. For now, I'll treat it as a welcome addition to my security arsenal, but I'll keep my password manager close at hand.
If you're curious about the shift towards a passwordless future, why not dip your toes in the water and give WebAuthn a test drive? Experiencing its potential firsthand might just make you a believer in a world less dependent on passwords. Several demos are available online where you can safely experiment with the registration and authentication processes. One such example is the [WebAuthn.io](https://webauthn.io/) demo developed by Duo Security, which is a user-friendly way to understand the mechanics of WebAuthn in a controlled, educational environment. As with any new technology, there's no substitute for hands-on experience, and I encourage you to explore these demos and consider the possibilities for your web applications.
We've written a lot about WebAuthn and passwordless authentication. If you're interested in learning more, check out our open-source [WebAuthn.wtf](https://webauthn.wtf/) site, where we explore the history, mechanics, and future of WebAuthn.
-Finally, I recommend consulting the [WebAuthn specification](https://www.w3.org/TR/webauthn-3/) if you're looking to learn more about specific technical details of WebAuthn. Taken as a whole the specification is daunting, but it is easy to find specific explanations and examples for each step of the process.
+Finally, I recommend consulting the [WebAuthn specification](https://www.w3.org/TR/webauthn-3/) if you're looking to learn more about specific technical details of passkeys. Taken as a whole the specification is daunting, but it is easy to find specific explanations and examples for each step of the process.
diff --git a/astro/src/content/articles/identity-basics/open-source-vs-commercial.md b/astro/src/content/articles/identity-basics/open-source-vs-commercial.md
index ba0da607d0..2084767831 100644
--- a/astro/src/content/articles/identity-basics/open-source-vs-commercial.md
+++ b/astro/src/content/articles/identity-basics/open-source-vs-commercial.md
@@ -59,7 +59,7 @@ Open-source projects [may be more secure, especially over time](https://www.info
However, there are also [instances of vulnerabilities remaining open for months](https://github.com/keycloak/keycloak/pull/7612). But if the bug is severe and an open-source project is well supported or popular, the timeline may shrink to days or weeks.
-In the end, you (and the maintainers) are ultimately responsible for the security of the software. Just like with maintenance responsibility, if you don't have time for your team to frequently review the security issues as they're reported and fixed and then upgrade your system, this may mean that open source isn't the best option for you.
+In the end, you (and the maintainers) are ultimately responsible for the security of the software. Is [your blue team](/articles/security/blue-team-red-team-purple-team) up for maintaining this system and educating your developers on issues? Just like with maintenance responsibility, if you don't have time for your team to frequently review the security issues as they're reported and fixed and then upgrade your system, this may mean that open source isn't the best option for you.
### Cost
diff --git a/astro/src/content/articles/identity-basics/outsource-auth-system-blueprint.md b/astro/src/content/articles/identity-basics/outsource-auth-system-blueprint.md
index fb294993a4..b9e6a0dbfa 100644
--- a/astro/src/content/articles/identity-basics/outsource-auth-system-blueprint.md
+++ b/astro/src/content/articles/identity-basics/outsource-auth-system-blueprint.md
@@ -160,6 +160,8 @@ In talking to people in this area, it still doesn't hurt to emphasize:
* Standards certifications that your proposed auth system complies with (ISO 27001, SOC2, HIPAA BAAs, etc).
* **Actual laws** that your proposed auth system can help you comply with, as mentioned in the legal section above (GDPR/CCPA are big ones!).
+You may want to include both [blue team and red team members](/articles/security/blue-team-red-team-purple-team) in this discussion.
+
### Infrastructure
If your organization is large enough for a dedicated infrastructure team it's likely that you already have a robust auth solution, but there are cases where you have several internal apps with different auth systems and you want to standardize on a new outsourced auth system.
diff --git a/astro/src/content/articles/identity-basics/what-is-identity-proofing.md b/astro/src/content/articles/identity-basics/what-is-identity-proofing.md
index db3877219d..7909c763d2 100644
--- a/astro/src/content/articles/identity-basics/what-is-identity-proofing.md
+++ b/astro/src/content/articles/identity-basics/what-is-identity-proofing.md
@@ -75,9 +75,9 @@ Out-of-band (OOB) authentication uses another channel besides an online process
OOB, also a form of MFA, increases security by requiring the person to have access to an external account or another device in their possession. Using a separate channel makes it more difficult for an attacker to circumvent or intercept the proofing process because the attacker would have to access and compromise two separate communication mechanisms.
-### Biometric Verification
+### Biometric Verification and Passkeys
-Biometric verification is used to identify individuals based on unique physical characteristics such as fingerprints, voiceprints, retina scans, signature comparisons, and DNA. Once only common in the most stringent security settings, these types of verification are becoming regular features in the latest smartphones and digital devices.
+Biometric verification is used to identify individuals based on unique physical characteristics such as fingerprints, voiceprints, retina scans, signature comparisons, and DNA. Once only common in the most stringent security settings, these types of verification are becoming regular features in the latest smartphones and digital devices. As [passkeys](/tech-papers/why-passkeys-improve-user-security-how-to-implement-them) see wider adoption, the use of biometrics continues to rise as well.
Biometrics give greater assurance that a person is who they say they are. The verification is used in several industries, including healthcare to identify patients, law enforcement to track people in the judicial system, and government entities for voter registration.
diff --git a/astro/src/content/articles/security/blue-team-red-team-purple-team.md b/astro/src/content/articles/security/blue-team-red-team-purple-team.md
index 59aa52568e..d8566dac46 100644
--- a/astro/src/content/articles/security/blue-team-red-team-purple-team.md
+++ b/astro/src/content/articles/security/blue-team-red-team-purple-team.md
@@ -9,11 +9,11 @@ date: 2024-03-05
dateModified: 2024-03-05
---
-Are you a leader seeking a basic framework for building a cohesive team in your company? Or perhaps you're an employee looking to understand the process of team building? This article offers insights that can re-energize your company's posture through cybersecurity principles applied to team dynamics. It caters to anyone interested in adopting a team-oriented approach to cybersecurity, which not only enhances company culture and communication but also boosts overall employee happiness.
+Are you a leader building a cohesive security function at your company? Or perhaps you're an employee looking to understand security teams? This article is for you.
After introducing the fundamental concepts of the Blue Team and Red Team, this article explores specific tools employed by each. The roles of Blue and Red Teams are examined in detail, featuring a real-world scenario orchestrated by a Red Team, complemented by an imagined Blue Team response. This practical example provides valuable insights into the decision-making processes of companies regarding security.
-Furthermore, the article introduces the concept of a 'Purple Team,' emphasizing the significance of team culture and company posture. Serving as a liaison between Blue and Red Teams, Purple Teams actively participate in both offensive and defensive strategies, working collaboratively towards the shared objective of protecting assets and human lives.
+Furthermore, the article discusses a Purple Team and what it brings, emphasizing the significance of team culture and company posture. Serving as a liaison between Blue and Red Teams, Purple Teams actively participate in both offensive and defensive strategies, working collaboratively towards the shared objective of protecting assets and human lives.
Let's kick things off!
@@ -63,13 +63,13 @@ A [2020 case study](https://www.dionach.com/wp-content/uploads/2020/03/Red-Team-
The first step to any successful plan is reconnaissance. In this case, the Red Team immersed themselves into various sources to gather and analyze information about the target company. Their reconnaissance strategies included researching social media profiles, identifying publicly accessible services, noting the company's physical address, exploring mobile application platforms, and seeking avenues for remote access.
-Through their comprehensive research, the Red Team uncovered crucial insights, including the authentication policies of the target organization. They discovered that employees utilized multi-factor authentication (MFA) to access their emails on shared platforms like Outlook. However, they also found that MFA was not a mandatory requirement for employees.
+Through their comprehensive research, the Red Team uncovered crucial insights, including the authentication policies of the target organization. They discovered that employees utilized [multi-factor authentication (MFA)](/features/multifactor-authentication) to access their emails on shared platforms like Outlook. However, they also found that MFA was not a mandatory requirement for employees.
Despite the absence of a mandate for MFA the Red Team refrained from pursuing a brute force attack. Brute force attacks involve relentless trial and error in attempting to breach a system or network. They initiated a phishing campaign instead. The phishing campaign was intended to compromise systems from an external source. Their goal was to acquire credentials from employees. However, their phishing attempts yielded no credentials. Consequently, they devised a plan B.
Successful attackers often use multiple approaches. Their social engineering plan included the creation of a backdoor, a method to bypass standard security measures and gain unauthorized entry. Two members of Dianoch's Red Team attended a company event, snatching the opportunity for further exploration with the goal to establish a foothold. While one team member was on the lookout to protect his partner, the other covertly distanced himself from the main event, searching for unlocked and unattended rooms. This strategy resulted in a significant breakthrough: upon discovering an unlocked room, he then had time and space to successfully access the network using his mobile phone. Subsequently, he proceeded to gather more comprehensive and specific information about the company's operations.
-Persistence proved beneficial for this Red Team. Now that the Red Team gained a foothold on the internal network of Dinoch, over several days, the team successfully captured password hashes. Hash functions are one-way cryptographic processes that transform human-readable data into unique strings.
+Persistence proved beneficial for this Red Team. Now that the Red Team gained a foothold on the internal network of Dinoch, over several days, the team successfully captured [password hashes](/docs/reference/password-hashes). Hash functions are one-way cryptographic processes that transform human-readable data into unique strings.
These clever hackers persisted despite encountering another obstacle: complex and lengthy passwords that prevented them from cracking hashes. With the network already compromised, the team pivoted and executed lateral movement by targeting an individual user to obtain their hash and gain access. By obtaining the user's hash, Dianoch's Red Team successfully accessed an administrative account without the need to crack the hash. Following their success, they executed another phishing campaign, this time from a trusted source, exploiting their access.
@@ -93,7 +93,7 @@ The persistence of the Red Team finally led to a breach when they used a single
Once the Blue Team identifies the attack and its source, they are able to isolate compromised systems, disable accounts, change passwords, and move the network offline. Blue Team forensics play an integral role in the identification process, procedures, and attack vectors. Logging, reporting and recovery are all other critical aspects of the Blue Team's responsibilities.
-Dinoch's situation highlights the importance of securing not only digital access controls but also physical ones. A Blue Team's post-incident report can detail physical security measures such as locking empty workspaces and unused rooms. A Zero-trust framework covers both physical and digital security needs, requiring all users inside and outside the network to be authorized and authenticated. This can include identification badges for event attendees and implementing a check-in registry to admit only authorized guests. Continuous training with social engineering emulations, patching and updates, spam and web filters, and antivirus solutions can help reduce the success and severity of a phishing attack. The key takeaway is that security is a complex and shared responsibility.
+Dinoch's situation highlights the importance of securing not only digital access controls but also physical ones. A Blue Team's post-incident report can detail physical security measures such as locking empty workspaces and unused rooms. A Zero-trust framework covers both physical and digital security needs, requiring all users inside and outside the network to be authorized and authenticated. This can include identification badges for event attendees and implementing a check-in registry to admit only authorized guests. Continuous training with social engineering emulations, patching and updates, spam and web filters, and antivirus solutions can help reduce the success and severity of a phishing attack. Using modern phishing-resistent solutions like [passkeys can help you avoid such attacks entirely](/guides/what-is-a-passkey). The key takeaway is that security is a complex and shared responsibility.
Below is a visualization of the NIST incident response lifecycle that Blue Teams follow for structured defensive applications:
diff --git a/astro/src/content/blog/passkeys-webinar.mdx b/astro/src/content/blog/passkeys-webinar.mdx
new file mode 100644
index 0000000000..ccfeb9fac4
--- /dev/null
+++ b/astro/src/content/blog/passkeys-webinar.mdx
@@ -0,0 +1,34 @@
+---
+publish_date: 2024-03-20
+title: Free Webinar - Passkeys Are The Path to Enhanced Digital Security
+description: Join us for a free webinar to learn about passkeys, how they work, and how they can help set your business apart.
+authors: FusionAuth
+image: /img/blogs/passkeys-webinar/passkeys-webinar.png
+categories: Education
+tags: passkeys, webinar
+excerpt_separator: "{/* more */}"
+---
+Wouldn't it be nice if you never had to click on a "forgot password" link again? Taking that dream a step further, what if you never had to come up with a password at all? [Passkeys](/guides/what-is-a-passkey) are the solution that makes these dreams possible. Ready to learn more? [Sign up now](https://go.fusionauth.io/value-of-passkeys) for our free webinar.
+
+## The Downfall of Passwords
+
+The fact is, passwords should have died long ago. Their inherent insecurity, made worse by user password fatigue, should be reason enough to bid them adieu. But that leaves out the important discussions around company resources, security breaches, and so much more.
+
+It's time for something better. For that matter, it's been time for...a long time. And that something better is the passkey.
+
+## What Are Passkeys
+Passkeys are a form of authentication that can augment or completely replace passwords. As only one of the two keys lives on a server, they're inherently more secure than passwords. Often times the private key (held by the user) can only be accessed by a fingerprint or facial recognition, enforcing even greater security measures.
+
+Microsoft, Apple, and Google rolled out support for passkeys in 2022. Since then, many major companies have joined in the effort. You'll now find passkey logins for Facebook, LinkedIn, Best Buy, Nintendo, Shopify, and many more. That said, the transition is still slower than it needs to be to say farewell to passwords.
+
+## The Advantages of Passkeys
+The list of advantages for passkeys is large and growing. They're more secure by their very nature. When implemented well, they provide a seamless user experience. What's even more impressive is that they share that same experience across devices.
+
+One area that doesn't get a lot of air time is how passkeys can improve product adoption. By eliminating friction and password fatigue, passkeys make it dead simple to log in and never have to remember a password. We're already seeing big names like Best Buy, Robinhood, Adobe and more adopt passkeys. It's a decision that, no doubt, is driven at some level by increased adoption.
+
+## Passkeys Unlocked
+We've only touched the surface of passkeys, but we want to tell you more. So we're hosting a [free webinar](https://go.fusionauth.io/value-of-passkeys) where we're going to dive into their history, benefits, use cases, and why passkeys are setting businesses apart.
+[![Passkeys webinar](/img/blogs/passkeys-webinar/webinar-alex.jpg)](https://go.fusionauth.io/value-of-passkeys)
+We're not going to get into the technical nitty gritty in this webinar. We'll save that for another time. This webinar covers use cases for passkeys in 2024 and beyond. So engineers, invite your product owners, security folks, and UX designers.
+
+Staying ahead of threats is table stakes these days. You simply don't want to be the next name in the news when a security breach happens because of password fatigue. Join us on April 11th at 10am PT, 1pm ET. You won't want to miss this webinar so [grab your spot](https://go.fusionauth.io/value-of-passkeys) today!
diff --git a/astro/src/content/blog/quantum-computing.mdx b/astro/src/content/blog/quantum-computing.mdx
new file mode 100644
index 0000000000..07a12f56fa
--- /dev/null
+++ b/astro/src/content/blog/quantum-computing.mdx
@@ -0,0 +1,63 @@
+---
+publish_date: 2024-04-01
+title: "FusionAuth Cracks the Code: Quantum Passwords and a Universe of Secure Logins"
+description: How FusionAuth has built the world's first Quantum Vault
+authors: Alex Patterson
+image: /img/blogs/quantum-computing/quantum.png
+categories: News
+tags: community story, april fools
+excerpt_separator: "{/* more */}"
+---
+Ever since the show [Devs](https://www.wired.com/story/inside-devs-dreamy-silicon-valley-quantum-thriller/) came out on FX in 2020, FusionAuth’s CEO Brian Pontarelli has been musing on the possibilities of adding quantum computing to FusionAuth. Four years ago, FusionAuth commissioned a Skunkworks that went into deep secrecy and worked on this concept. After years of iterating on both the physical design and the software to accompany the solution we're thrilled to announce a revolutionary new advancement in the realm of user authentication: **The FusionAuth Quantum Vault**.
+
+{/* more */}
+
+## How Quantum Vulnerabilities Impact JWTs
+
+[JSON Web Tokens](/articles/tokens/) (JWTs) are a popular way to securely share information online. However, traditional JWTs that rely on certain encryption methods could be broken by powerful quantum computers. These attacks primarily apply to JWTs that use asymmetric cryptography – where your login info is validated against a public key. The issue? Future quantum computers can theoretically break these encryption methods by using alternate pathways to, for example, factor large prime numbers.
+
+## The FusionAuth Quantum Vault Solution
+
+The FusionAuth Quantum Vault reimagines how JWTs could work in the quantum era.
+
+Here's what it offers:
+
+* **Advanced Quantum-Resistant Algorithms:** Instead of relying on traditional encryption, the FusionAuth Quantum Vault uses next-generation, quantum-resistant algorithms. This ensures that even if quantum computers become widespread, your JWTs will remain secure.
+* **Beyond Just Encryption:** While quantum-safe encryption is vital, the FusionAuth Quantum Vault goes further. We believe the future of authentication lies in minimizing the amount of sensitive data customers need to store anywhere. Quantum entanglement reduces the need for signing keys.
+* **Password superposition** When used carefully, the concept of superposition can offer efficiencies, allowing users to log in to multiple applications at one time. Or maybe letting many users log in to one application at once. We're not quite sure, we didn't really understand the double slit experiment.
+
+The FusionAuth Quantum Vault doesn't just patch a hole; it aims to rethink how JWTs and authentication can evolve for a more secure future. We're looking beyond just encryption and exploring new token models to keep your data safe in the face of evolving threats.
+
+## What In The Qubit?
+
+You might be thinking, "Quantum computers? Those things are still in their infancy, barely clinging to the shores of theoretical physics!" But fear not, intrepid reader, because the brilliant minds at FusionAuth have been working tirelessly in our secret lab (complete with beanbag chairs and a kombucha fountain, of course) to develop a quantum authentication system that would make even Schrödinger's cat do a double meow. (Yeah, the cat lives.)
+
+## So, How Does This Quantum Contraption Work?
+
+Imagine a password, not as a string of characters, but as a probability wave function. In the quantum realm, a password can exist in multiple states simultaneously, until the act of logging in collapses the wave function into a single, verified state. This means that brute-force attacks become a thing of the past, as hackers would be grappling with not just one password, but a superposition of infinite possibilities.
+
+## But wait, there's more!
+
+The FusionAuth Quantum Vault isn't just about impenetrable security. It's also about scalability. With the mind-bending power of qubits, our system can handle an unimaginable number of users – **up to 40,024,291,137, to be precise**. That's more than the entire population of the Earth! So, whether you're a social media platform with millions of users or a niche cat video forum with a dedicated following of twelve, the FusionAuth Quantum Vault can handle your login needs.
+
+## The Ethical Implications of Quantum Authentication
+
+Of course, with great power comes great responsibility. We understand that the idea of a quantum computer wielding the keys to billions of passwords might raise some eyebrows. But fear not, privacy advocates! The FusionAuth Quantum Vault operates on a zero-knowledge principle. This means that your password never leaves your device in its original form. Instead, it's transformed into a cryptographic hash that only you and the vault can use.
+
+Google has most recently published an [article](https://bughunters.google.com/blog/5108747984306176/google-s-threat-model-for-post-quantum-cryptography) outlining the fact, "if we do not encrypt our data with a quantum-secure algorithm right now, an attacker who is able to store current communication will be able to decrypt it in as soon as a decade". This was exactly our motivation for making one of the most secure authentication systems to utilize **The FusionAuth Quantum Vault** so that your data will remain secure for the foreseeable future.
+
+Good one Google! You're only four years behind, keep trying!
+
+## When Will The FusionAuth Quantum Vault be Available?
+
+**The FusionAuth Quantum Vault** will change the world of online security as we know it. We are currently running a googolplexianth number of tests on our prototype vault pictured below.
+
+![Photo of FusionAuth Quantum Vault](/img/blogs/quantum-computing/Gemini_Generated_Image.jpeg)
+
+For now, consider this a glimpse into the future of authentication – a future where JWTs are safe and logins are instantaneous, secure, and scalable beyond imagination.
+
+**P.S.**
+
+While the FusionAuth Quantum Vault might not be here just yet, we are constantly innovating to bring you the most cutting-edge authentication solutions available. Check out our latest [resources](/resource/all) to learn more about how FusionAuth can keep your data safe and secure.
+
+**P.P.S.** Happy April Fools!
diff --git a/astro/src/content/blog/understanding-multifactor-authentication-mfa.mdx b/astro/src/content/blog/understanding-multifactor-authentication-mfa.mdx
new file mode 100644
index 0000000000..75b8e0d6ba
--- /dev/null
+++ b/astro/src/content/blog/understanding-multifactor-authentication-mfa.mdx
@@ -0,0 +1,64 @@
+---
+publish_date: 2024-03-01
+title: A Quick Explanation of Multi-Factor Authentication (MFA)
+description: There's a big "You Are Here" arrow on our timeline of security. It points to multi-factor authentication.
+image: /img/blogs/understanding-mfa/understanding-mfa.png
+categories: Education
+authors: Brad McCarty
+tags: security, mfa
+excerpt_separator: "{/* more */}"
+---
+
+Picture a timeline where the starting point is the very first person entering a username and password combination. As the line moves on, you see how the world of user login has changed. Then there's a big "You Are Here" arrow. It points to an area labeled [multi-factor authentication](/articles/authentication/multi-factor-authentication).
+
+Now yes, you could argue that we're well beyond MFA already. To some extent, you'd be right. But it's also accurate to say that MFA covers a wide swath of the timeline, and we're still inside of its boundaries today. While we are moving toward a future where [passwords go away](/articles/authentication/passwordless-authentication-security), we're still working to get MFA done right. Adoption is still [under 40 percent](https://www.resmo.com/blog/multifactor-authentication-statistics) in small businesses; a worrying statistic when you also grasp that nearly 60 percent of people still write down passwords on sticky notes. Of those, almost 70 percent [admit](https://www.lastpass.com/state-of-the-password/global-password-security-report-2019) to losing the notes at some point.
+
+As MFA solutions are enjoying their peak, it's a good time to dive in and get a better understanding of the landscape we're in today.
+
+## Understanding Multi-Factor Authentication
+
+Multi-factor authentication (MFA) does what it says on the box. It requires users to provide two or more verification "factors" to gain access to a resource. To be clear, two-factor authentication is also a form of multi-factor authentication. In fact, it's the most common.
+
+Anyway, those factors may be an application, an online account, or a service like a VPN. Traditional authentication methods rely on a single factor--something you know (like a password.) MFA adds extra layers of security by requiring something you have (like an authenticator app or SMS with a one-time password on a mobile device), or something you are (like [a fingerprint](/blog/introducing-biometric-authentication)) to help keep cybercriminals, hackers, and data breaches at bay.
+
+This second factor and third factor make MFA a strong barrier against bad actors seeking unauthorized access. We live in a time where each week seems to bring [another news story](/blog/grammarly-proves-ciam-not-optional) of a security breach. If today is the most important time to have better security, tomorrow will be even more important.
+
+## The Importance of MFA in Cybersecurity
+
+It would be hard to overstate how important MFA is. As we mentioned earlier, we see new threats emerging all the time. These threats [grow more sophisticated](blog/fusionauth-releases-advanced-threat-detection), and the limitations of simple password protection becomes glaringly evident. While passwords were once considered to be the highest form of security, they're now susceptible to common vulnerabilities. Phishing attacks, social engineering, and brute force methods are like a wrecking ball against a glass house. Not to mention the practice of passwords on sticky notes, which is like leaving a key under your doormat...with a sign pointing to it that says "KEY".
+
+Multi-factor authentication addresses these vulnerabilities. So now, even if a user finds their password compromised, unauthorized users still have more layers to overcome. It's somewhat obvious to see how important this is for individuals. But the real story is that users are often single points of failure for larger organizations. MFA enables these organizations to protect themselves from breaches or other attacks by bolstering security at its weakest link.
+
+## The Authentication Methods at Your Disposal
+
+When we talk about the factors available in MFA, we're referring to the various forms of verification used in conjunction with one another. These fall into three categories.
+
+1. Something You Know - This includes passwords, PINs, or answers to security questions. It's the most traditional form of authentication but also the most vulnerable when used by itself.
+
+2. Something You Have - This category covers devices or items in your possession that can generate or receive a verification code. Apps like the Google Authenticator, a device like a Yubikey, or even push notifications sent directly to a phone fit into this category. The use of a device provides a physical layer of security, making unauthorized access more challenging.
+
+3. Something You Are - This involves biometric verification. It's quite literally answering the question of "who are you?" These methods include fingerprint scans, facial recognition, or iris scans. Some services are even using voice intonation as a form of biometrics. Biometrics are highly secure because they are unique to each person. They're also inherently complex, and hard to replicate.
+
+By leveraging a combination of these factors, MFA plays a crucial role in creating security measures that significantly reduces the risk of unauthorized access while still keeping user experience in mind.
+
+## When to Implement Multi-Factor Authentication
+
+Deciding when to require MFA involves a careful assessment of risk, convenience, and the value of the protected assets. Generally, MFA is crucial in the following scenarios:
+
+***Access to Sensitive Information:*** Whenever access to sensitive personal or business information is involved, MFA is non-negotiable. This includes financial data, personal records, or proprietary business information.
+
+***Remote Access:*** With the rise of remote work, securing remote access to company networks is paramount. MFA provides an additional security layer. It ensures that only authorized users can access the network from various locations.
+
+***Compliance Requirements:*** Certain industries and regulations mandate the use of MFA and other [compliance requirements](/articles/authentication/mfa-compliance-fusionauth) to protect sensitive data. For organizations subject to these regulations, implementing MFA is a legal necessity.
+
+***High-Profile Accounts:*** Accounts with elevated privileges or access to critical systems should always be protected with MFA. This ensures that even if a password is compromised, the system remains secure against unauthorized changes or breaches.
+
+Incorporating MFA has become increasingly user-friendly. Many platforms offer seamless MFA experiences that balance security with convenience, making it an accessible option for organizations of all sizes.
+
+## Beyond Multi-Factor Authentication
+
+While MFA represents a significant leap forward in authentication security, the journey doesn't end here. The landscape is continually evolving, with new technologies and methodologies on the horizon. We're already seeing the uptake of biometrics. AI and machine learning have ushered in behavioral analytics and adaptive MFA. Decentralized identity models promise to further redefine what it means to secure our digital identities.
+
+It's crucial to stay informed and adaptive, embracing advances that offer better security without sacrificing usability. Staying on top of all of this is a challenge, and it's why a growing number of companies are choosing [FusionAuth](/) to meet their MFA requirements. The balance between security and convenience will always be a moving target. With tools like MFA at our disposal, we're better equipped to face these challenges.
+
+Multi-factor authentication is a foundational pillar of security. It's essential for protecting our digital identities and sensitive information. As we continue to witness the evolution of authentication methods, MFA will undoubtedly play a pivotal role in shaping future security protocols.
diff --git a/astro/src/content/docs/_shared/_hosted-backend-warning.md b/astro/src/content/docs/_shared/_hosted-backend-warning.md
index 3d2f67cbbb..2a36f0ca24 100644
--- a/astro/src/content/docs/_shared/_hosted-backend-warning.md
+++ b/astro/src/content/docs/_shared/_hosted-backend-warning.md
@@ -7,7 +7,7 @@ If possible, have FusionAuth and your application served by the same domain, usi
If this configuration is not possible, use one of these alternative methods:
* Develop using a local FusionAuth instance, so both your webapp and FusionAuth are running on `localhost`.
-* Do not use the FusionAuth hosted backend, and instead write your own backend with a cross origin cookie policy: [here's an example](https://github.com/FusionAuth/fusionauth-example-react-sdk/tree/main/server).
+* Do not use the FusionAuth hosted backend, and instead write your own backend with a cross origin cookie policy: [here's an example](https://github.com/FusionAuth/fusionauth-javascript-sdk-express/tree/main).
* Configure a [custom domain name for the FusionAuth Cloud instance](/docs/get-started/run-in-the-cloud/cloud#updating-with-existing-custom-domains) (limited to certain plans).
Modifying FusionAuth CORS configuration options does not fix this issue because the cookies that FusionAuth writes will not be accessible cross domain.
diff --git a/astro/src/content/docs/_shared/_release-notification.mdx b/astro/src/content/docs/_shared/_release-notification.mdx
index ab4f8fa63c..d9e0d9d951 100644
--- a/astro/src/content/docs/_shared/_release-notification.mdx
+++ b/astro/src/content/docs/_shared/_release-notification.mdx
@@ -2,8 +2,7 @@ There are a number of ways to be notified of new releases.
* The [release notes](/docs/release-notes) are updated when there is a new release or shortly thereafter.
* There is an [RSS feed of releases](/docs/releases.xml) to which you can subscribe.
-* The [blog has release announcement posts](/blog), and you can subscribe to an [RSS feed](/blog/feed.xml) for that as well.
-* The [forum has release announcement posts](/community/forum/category/5/release).
+* The [blog has release announcement posts](/blog/tag/release-notes/) and you can subscribe to an [RSS feed](/blog/feed.xml).
* You can subscribe to our release announcement email list using the form below.
diff --git a/astro/src/content/docs/apis/_api-key-cross-tenant-note.mdx b/astro/src/content/docs/apis/_api-key-cross-tenant-note.mdx
new file mode 100644
index 0000000000..85a91584b2
--- /dev/null
+++ b/astro/src/content/docs/apis/_api-key-cross-tenant-note.mdx
@@ -0,0 +1 @@
+Tenant scoped keys can retrieve configuration for FusionAuth entities such as identity providers and lambdas that may be shared between tenants. Limit the API key by specifying required endpoints and permissions as well as the tenant.
diff --git a/astro/src/content/docs/apis/_apikey-post-put-request-body.mdx b/astro/src/content/docs/apis/_apikey-post-put-request-body.mdx
index c099a923c0..18115bb74e 100644
--- a/astro/src/content/docs/apis/_apikey-post-put-request-body.mdx
+++ b/astro/src/content/docs/apis/_apikey-post-put-request-body.mdx
@@ -1,5 +1,6 @@
import APIBlock from 'src/components/api/APIBlock.astro';
import APIField from 'src/components/api/APIField.astro';
+import APIKeyCrossTenantNote from 'src/content/docs/apis/_api-key-cross-tenant-note.mdx';
import JSON from 'src/components/JSON.astro';
import EnterpriseEditionBlurbApi from 'src/content/docs/_shared/_enterprise-edition-blurb-api.astro';
@@ -22,9 +23,11 @@ import EnterpriseEditionBlurbApi from 'src/content/docs/_shared/_enterprise-edit
The unique Id of the Tenant. This value is required if the key is meant to be tenant scoped. Tenant scoped keys can only be used to access users and other tenant scoped objects for the specified tenant. This value is read-only once the key is created.
+
+
{ props.apikey_create_request && }
-{ props.apikey_update_request && }
\ No newline at end of file
+{ props.apikey_update_request && }
diff --git a/astro/src/content/docs/apis/_application-request-body.mdx b/astro/src/content/docs/apis/_application-request-body.mdx
index 908d440574..9d61fa166a 100644
--- a/astro/src/content/docs/apis/_application-request-body.mdx
+++ b/astro/src/content/docs/apis/_application-request-body.mdx
@@ -21,7 +21,7 @@ import AdvancedEditionBlurbApi from 'src/content/docs/_shared/_advanced-edition-
The Id of the [IP Access Control List](/docs/apis/ip-acl) limiting access to this application.
-
+
Determines if Users can have Authentication Tokens associated with this Application. This feature may not be enabled for the FusionAuth application.
@@ -42,7 +42,7 @@ import AdvancedEditionBlurbApi from 'src/content/docs/_shared/_advanced-edition-
-
+
The time in seconds until an issued Two Factor trust Id is no longer valid and the User will be required to complete Two Factor authentication
@@ -55,12 +55,12 @@ import AdvancedEditionBlurbApi from 'src/content/docs/_shared/_advanced-edition-
When enabled a user will be required to provide their current password when changing their password on a self-service account form.
-
+
The unique Id of the form to enable authenticated users to manage their profile on the account page.
-
+
The Id of the signing key used to sign the access token.
@@ -137,7 +137,7 @@ import AdvancedEditionBlurbApi from 'src/content/docs/_shared/_advanced-edition-
The unique Id of the lambda that will be used to perform additional validation on registration form steps.
-
+
Indicates if a JWT may be refreshed using a Refresh Token for this application. This configuration is separate from issuing new Refresh Tokens which is controlled by the `generateRefreshTokens` parameter. This configuration indicates specifically if an existing Refresh Token may be used to request a new JWT using the [Refresh API](/docs/apis/jwt#refresh-a-jwt).
@@ -157,7 +157,7 @@ import AdvancedEditionBlurbApi from 'src/content/docs/_shared/_advanced-edition-
The Id of the email template that is used when notifying a user to complete a multi-factor authentication request.
-
+
When enabled and a user has one or more two-factor methods configured, the user will be required to complete a two-factor challenge during login. When disabled, even when a user has configured one or more two-factor methods, the user will not be required to complete a two-factor challenge during login. When required, the user will be required to complete a two-factor challenge during login.
When configured, this value overrides the value configured by the tenant.multiFactorConfiguration.loginPolicy.
@@ -167,6 +167,8 @@ import AdvancedEditionBlurbApi from 'src/content/docs/_shared/_advanced-edition-
* `Enabled` - Require a two-factor challenge during login when an eligible method is available.
* `Disabled` - Do not require a two-factor challenge during login.
* `Required` - Require a two-factor challenge during login. A user will be required to configure 2FA if no eligible methods are available. Available since 1.42.0
+
+ While this configuration requires a license, in version `1.49.0` or later it may be enabled for the FusionAuth admin application regardless of the license state.
The Id of the SMS template that is used when notifying a user to complete a multi-factor authentication request.
@@ -562,7 +564,7 @@ import AdvancedEditionBlurbApi from 'src/content/docs/_shared/_advanced-edition-
The unique Id of the theme to be used to style the login page and other end user templates.
-
+
The Id of the Email Template that is used to send the Registration Verification emails to users. If the `verifyRegistration` field is `true` this field is required.
@@ -572,15 +574,15 @@ import AdvancedEditionBlurbApi from 'src/content/docs/_shared/_advanced-edition-
Whether the WebAuthn bootstrap workflow is enabled for this application. This overrides the tenant configuration. Has no effect if application.webAuthnConfiguration.enabled is `false`.
-
+
Indicates if this application enables WebAuthn workflows based on the configuration defined here or the Tenant WebAuthn configuration. If this is `false`, WebAuthn workflows will be enabled based on the Tenant configuration. If `true`, WebAuthn workflows will be enabled according to the configuration of this application.
-
+
Whether the WebAuthn reauthentication workflow is enabled for this application. This overrides the tenant configuration. Has no effect if application.webAuthnConfiguration.enabled is `false`.
-
+
An array of Webhook Ids. For Webhooks that are not already configured for All Applications, specifying an Id on this request will indicate the associated Webhook should handle events for this application.
diff --git a/astro/src/content/docs/apis/_tenant-application-email-configuration.mdx b/astro/src/content/docs/apis/_tenant-application-email-configuration.mdx
index 4398cafd64..818b3da142 100644
--- a/astro/src/content/docs/apis/_tenant-application-email-configuration.mdx
+++ b/astro/src/content/docs/apis/_tenant-application-email-configuration.mdx
@@ -25,7 +25,7 @@ import InlineField from 'src/components/InlineField.astro';
The Id of the Email Template used to send emails to users when their email address is updated. {props.application_email_config_override_text}
- {props.show_feature_blurb && }
+ {props.show_feature_blurb && }
@@ -49,7 +49,7 @@ import InlineField from 'src/components/InlineField.astro';
The Id of the Email Template used to send emails to users when another user attempts to create an account with their login Id. {props.application_email_config_override_text}
- {props.show_feature_blurb && }
+ {props.show_feature_blurb && }
@@ -59,13 +59,13 @@ import InlineField from 'src/components/InlineField.astro';
The Id of the Email Template used to send emails to users when they log in on a new device. {props.application_email_config_override_text}
- {props.show_feature_blurb && }
+ {props.show_feature_blurb && }
The Id of the Email Template used to send emails to users when a suspicious login occurs. {props.application_email_config_override_text}
- {props.show_feature_blurb && }
+ {props.show_feature_blurb && }
@@ -79,13 +79,13 @@ import InlineField from 'src/components/InlineField.astro';
The Id of the Email Template used to send emails to users when they have completed a 'forgot password' workflow and their password has been reset. {props.application_email_config_override_text}
- {props.show_feature_blurb && }
+ {props.show_feature_blurb && }
The Id of the Email Template used to send emails to users when their password has been updated. {props.application_email_config_override_text}
- {props.show_feature_blurb && }
+ {props.show_feature_blurb && }
@@ -113,13 +113,13 @@ import InlineField from 'src/components/InlineField.astro';
The Id of the Email Template used to send emails to users when a MFA method has been added to their account. {props.application_email_config_override_text}
- {props.show_feature_blurb && }
+ {props.show_feature_blurb && }
The Id of the Email Template used to send emails to users when a MFA method has been removed from their account. {props.application_email_config_override_text}
- {props.show_feature_blurb && }
+ {props.show_feature_blurb && }
diff --git a/astro/src/content/docs/apis/_tenant-authentication.mdx b/astro/src/content/docs/apis/_tenant-authentication.mdx
index f96c1fd927..2f0ea17fa2 100644
--- a/astro/src/content/docs/apis/_tenant-authentication.mdx
+++ b/astro/src/content/docs/apis/_tenant-authentication.mdx
@@ -1,3 +1,4 @@
+import APIKeyCrossTenantNote from 'src/content/docs/apis/_api-key-cross-tenant-note.mdx';
import Aside from 'src/components/Aside.astro';
## Making an API Request Using a Tenant Id
@@ -28,9 +29,7 @@ curl -v -X POST \
You may optionally create an API key scoped to a particular tenant.
Below, we have selected the `Pied Piper` tenant for this API key. Only Users, Groups and Applications belonging to the `Pied Piper` tenant will be visible to this API.
diff --git a/astro/src/content/docs/apis/_tenant-request-body.mdx b/astro/src/content/docs/apis/_tenant-request-body.mdx
index 86aac21815..451815379a 100644
--- a/astro/src/content/docs/apis/_tenant-request-body.mdx
+++ b/astro/src/content/docs/apis/_tenant-request-body.mdx
@@ -20,7 +20,7 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
The Id of the [IP Access Control List](/docs/apis/ip-acl) limiting access to all applications in this tenant.
-
+
@@ -28,27 +28,27 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
-
+
Whether captcha configuration is enabled.
-
+
The secret key for this captcha method. This field is required when tenant.captchaConfiguration.enabled is set to `true`.
-
+
The site key for this captcha method. This field is required when tenant.captchaConfiguration.enabled is set to `true`.
-
+
@@ -56,7 +56,7 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
The value must be between `0.0` and `1.0`. Values outside of that range will result in an error.
-
+
@@ -298,14 +298,14 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
The time in seconds until a WebAuthn authentication challenge is no longer valid and the User will be required to restart the WebAuthn authentication ceremony by creating a new challenge. This value also controls the timeout for the client-side WebAuthn `navigator.credentials.get` API call. Value must be greater than 0.
-
+
The time in seconds until a WebAuthn registration challenge is no longer valid and the User will be required to restart the WebAuthn registration ceremony by creating a new challenge. This value also controls the timeout for the client-side WebAuthn `navigator.credentials.create` API call. Value must be greater than 0.
-
+
@@ -385,7 +385,7 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
When this parameter is not provided, it will default to the form Id currently assigned to the Default tenant.
-
+
@@ -462,7 +462,7 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
The Id of a SCIM User Request Lambda that will be used to convert the SCIM Enterprise User request to a FusionAuth User.
-
+
Required when tenant.scimServerConfiguration.enabled is `true`.
@@ -470,7 +470,7 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
The Id of a SCIM User Response Lambda that will be used to convert a FusionAuth Enterprise User to a SCIM Server response.
-
+
Required when tenant.scimServerConfiguration.enabled is `true`.
@@ -478,7 +478,7 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
The Id of a SCIM Group Request Lambda that will be used to convert the SCIM Group request to a FusionAuth Group.
-
+
Required when tenant.scimServerConfiguration.enabled is `true`.
@@ -486,7 +486,7 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
The Id of a SCIM Group Response Lambda that will be used to convert a FusionAuth Group to a SCIM Server response.
-
+
Required when tenant.scimServerConfiguration.enabled is `true`.
@@ -494,7 +494,7 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
The Id of a SCIM User Request Lambda that will be used to convert the SCIM User request to a FusionAuth User.
-
+
Required when tenant.scimServerConfiguration.enabled is `true`.
@@ -502,7 +502,7 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
The Id of a SCIM User Response Lambda that will be used to convert a FusionAuth User to a SCIM Server response.
-
+
Required when tenant.scimServerConfiguration.enabled is `true`.
@@ -580,7 +580,7 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
The Id of a lambda that will be called to populate the JWT during a client credentials grant.
-
+
@@ -669,7 +669,7 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
Whether rate limiting is enabled for failed login.
-
+
@@ -678,7 +678,7 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
Required when `enabled` is set to `true`.
-
+
@@ -687,14 +687,14 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
Required when `enabled` is set to `true`.
-
+
Whether rate limiting is enabled for forgot password.
-
+
@@ -703,7 +703,7 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
Required when `enabled` is set to `true`.
-
+
@@ -712,14 +712,14 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
Required when `enabled` is set to `true`.
-
+
Whether rate limiting is enabled for send email verification.
-
+
@@ -728,7 +728,7 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
Required when `enabled` is set to `true`.
-
+
@@ -737,14 +737,14 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
Required when `enabled` is set to `true`.
-
+
Whether rate limiting is enabled for send passwordless.
-
+
@@ -753,7 +753,7 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
Required when `enabled` is set to `true`.
-
+
@@ -762,14 +762,14 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
Required when `enabled` is set to `true`.
-
+
Whether rate limiting is enabled for send registration verification.
-
+
@@ -778,7 +778,7 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
Required when `enabled` is set to `true`.
-
+
@@ -787,14 +787,14 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
Required when `enabled` is set to `true`.
-
+
Whether rate limiting is enabled for send two factor.
-
+
@@ -803,7 +803,7 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
Required when `enabled` is set to `true`.
-
+
@@ -812,21 +812,21 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
Required when `enabled` is set to `true`.
-
+
A list of unique domains that are not allowed to register when self service is enabled.
-
+
The Entity Type that will be used to represent SCIM Clients for this tenant.
-
+
Required when tenant.scimServerConfiguration.enabled is `true`.
@@ -834,7 +834,7 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
Whether or not this tenant has the SCIM endpoints enabled.
-
+
@@ -844,14 +844,14 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
When this parameter is not provided, it will default to EnterpriseUser, Group, and User schema definitions as defined by the SCIM core schemas spec.
-
+
The Entity Type that will be used to represent SCIM Servers for this tenant.
-
+
Required when tenant.scimServerConfiguration.enabled is `true`.
@@ -877,7 +877,7 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
When `true`, FusionAuth will handle username collisions by generating a random suffix.
-
+
@@ -905,17 +905,17 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
The recommended value for the bootstrap workflow is `any`.
-
+
-
+
Whether or not this tenant has the WebAuthn bootstrap workflow enabled. The bootstrap workflow is used when the user must "bootstrap" the authentication process by identifying themselves prior to the WebAuthn ceremony and can be used to authenticate from a new device using WebAuthn.
-
+
@@ -928,21 +928,21 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
It is _highly_ recommended to use the `required` option for the bootstrap workflow.
-
+
Determines if debug should be enabled for this tenant to create an event log to assist in debugging WebAuthn errors.
-
+
Whether or not this tenant has WebAuthn enabled globally.
-
+
@@ -955,17 +955,17 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
The recommended value for the reauthentication workflow is `platform`.
-
+
-
+
Whether or not this tenant has the WebAuthn reauthentication workflow enabled. The reauthentication workflow will automatically prompt a user to authenticate using WebAuthn for repeated logins from the same device.
-
+
@@ -978,7 +978,7 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
It is _highly_ recommended to use the `required` option for the reauthentication workflow.
-
+
@@ -987,7 +987,7 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
When this parameter is omitted, FusionAuth will use `null` for the Relying Party Id in passkey creation and request options. A `null` value in the [WebAuthn JavaScript API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API) will use the browser origin.
-
+
@@ -996,7 +996,7 @@ import TransactionTypes from 'src/content/docs/apis/_transaction-types.mdx';
When this parameter is omitted, FusionAuth will use the tenant.issuer value.
-
+
diff --git a/astro/src/content/docs/apis/_user-email-verification-ids-response.mdx b/astro/src/content/docs/apis/_user-email-verification-ids-response.mdx
new file mode 100644
index 0000000000..54ea345aac
--- /dev/null
+++ b/astro/src/content/docs/apis/_user-email-verification-ids-response.mdx
@@ -0,0 +1,20 @@
+import APIField from 'src/components/api/APIField.astro';
+import InlineField from 'src/components/InlineField.astro';
+
+
+ An email verification Id. When present, this will represent the user's current email verification Id.
+
+ When using `FormField` verification strategy, this is the first part of the pair of verification Ids, and the emailVerificationOneTimeCode is the second part. When `ClickableLink` is the verification strategy, this value is sensitive in that this value by itself can be used to verify the user's email address.
+
+ When `ClickableLink` is the verification strategy, this is the value that will have been emailed to the user in order to complete verification. If you have not configured SMTP, you may optionally use this value to use an out of band transport such as your own email service.
+
+ Prior to version `1.49.0`, this value was only returned when using `FormField` verification strategy. Beginning in `1.49.0` the value is always returned when available.
+
+
+
+ An email one time code that will be paired with the emailVerificationId.
+
+ When `FormField` is the verification strategy, this is the value that will have been emailed to the user in order to complete verification. If you have not configured SMTP, you may optionally use this value to use an out of band transport such as your own email service.
+
+ This value will only be present when using the `FormField` verification strategy. When present, this is the value the user will need to enter to complete email verification.
+
diff --git a/astro/src/content/docs/apis/_user-login-response.mdx b/astro/src/content/docs/apis/_user-login-response.mdx
new file mode 100644
index 0000000000..143563d248
--- /dev/null
+++ b/astro/src/content/docs/apis/_user-login-response.mdx
@@ -0,0 +1,29 @@
+import APIField from 'src/components/api/APIField.astro';
+import InlineField from 'src/components/InlineField.astro';
+
+
+ The pending identity provider link Id. This value is created when logging in with an identity provider configured with a linking strategy of Create a pending link. It will only be included in the response body when this strategy is configured and a link does not yet exist for the user. It is used in conjunction with the Link APIs to complete a pending link.
+
+
+
+ The refresh token that can be used to obtain a new access token once the provided one has expired.
+
+ Because a refresh token is per user and per application, this value will only be returned when an applicationId was provided on the login request and the user is registered to the application.
+
+ You must explicitly allow generation of refresh tokens when using the Login API.
+
+ Configure the application.loginConfiguration.generateRefreshTokens setting via the API or enable the setting by navigating to the Application -> My Application -> Security tab.
+
+
+
+ When the refreshToken is returned in the response, this field will also be returned. This unique Id is the persistent identifier for this refresh token, and will not change even when using one-time use refresh tokens. This value may optionally be used to revoke the token using the Refresh Token API.
+
+
+ If authenticated using a One Time Password and state was provided during the Change Password request this value will be returned exactly as it was provided.
+
+
+ If authenticated using Two Factor and state was provided during the Two Factor Start request this value will be returned exactly as it was provided.
+
+
+ If state was provided during the passwordless login send request this value will be returned exactly as it was provided.
+
diff --git a/astro/src/content/docs/apis/_user-memberships-response.mdx b/astro/src/content/docs/apis/_user-memberships-response.mdx
new file mode 100644
index 0000000000..ce800c268f
--- /dev/null
+++ b/astro/src/content/docs/apis/_user-memberships-response.mdx
@@ -0,0 +1,17 @@
+import APIField from 'src/components/api/APIField.astro';
+
+
+ The list of memberships for the User.
+
+
+ An object that can hold any information about the User for this membership that should be persisted.
+
+
+ The Id of the Group of this membership.
+
+
+ The unique Id of this membership.
+
+
+ The instant that the membership was created.
+
diff --git a/astro/src/content/docs/apis/_user-registration-response-body.mdx b/astro/src/content/docs/apis/_user-registration-response-body.mdx
index 7ad9fa6894..e463a1376e 100644
--- a/astro/src/content/docs/apis/_user-registration-response-body.mdx
+++ b/astro/src/content/docs/apis/_user-registration-response-body.mdx
@@ -63,7 +63,18 @@ import RemovedSince from 'src/components/api/RemovedSince.astro';
This value indicates if this User's registration has been verified.
- When registration verification is enabled, this value will be returned. This is the value that will have been emailed to the user in order to complete verification.
+ When registration verification is enabled, this value will be returned if the registration has not yet been verified.
+
+ When using FormField verification strategy, this is the first part of the pair of verification Ids, and the registrationVerificationOneTimeCode is the second part. When ClickableLink is the verification strategy, this value is sensitive in that this value by itself can be used to verify the registration.
+
+ When ClickableLink is the verification strategy, this is the value that will have been emailed to the user in order to complete verification. If you have not configured SMTP, you may optionally use this value to use an out of band transport such as your own email service.
+
+ Prior to version 1.49.0, this value was only returned when using FormField verification strategy. Beginning in 1.49.0 the value is always returned when available.
+
+
+ A registration one time code that will be paired with the registrationVerificationId.
+
+ This value will only be present when when FormField is configured as the verification strategy. When present, this value will have been emailed to the user in order to complete verification. If you have not configured SMTP, you may optionally use this value to use an out of band transport such as your own email service.
The access token, this string is an encoded JSON Web Token (JWT).
diff --git a/astro/src/content/docs/apis/_user-registration-verification-ids-response.mdx b/astro/src/content/docs/apis/_user-registration-verification-ids-response.mdx
new file mode 100644
index 0000000000..fa11703687
--- /dev/null
+++ b/astro/src/content/docs/apis/_user-registration-verification-ids-response.mdx
@@ -0,0 +1,20 @@
+import APIField from 'src/components/api/APIField.astro';
+import InlineField from 'src/components/InlineField.astro';
+
+
+ A map of registration verification Id keyed by the `application`. When present, this will represent the user's current registration verification Ids.
+
+ When using `FormField` verification strategy, this is the first part of the pair of verification Ids, and the one time code in the registrationVerificationOneTimeCodes map is the second part. When `ClickableLink` is the verification strategy, this value is sensitive in that this value by itself can be used to verify the user's registration.
+
+ When `ClickableLink` is the verification strategy, you may optionally use this value to use an out of band transport such as your own email service.
+
+ Prior to version `1.49.0`, this value was only returned when using `FormField` verification strategy. Beginning in `1.49.0` the value is always returned when available.
+
+
+
+ A registration one time code that will be paired with the verification Id in the registrationVerificationIds map.
+
+ When `FormField` is the verification strategy, you may optionally use this value to use an out of band transport such as your own email service.
+
+ This value will only be present when using the `FormField` verification strategy. When present, this is the value the user will need to enter to complete registration verification.
+
diff --git a/astro/src/content/docs/apis/_user-response-body.mdx b/astro/src/content/docs/apis/_user-response-body.mdx
index 98f4f0d552..c15a413e3c 100644
--- a/astro/src/content/docs/apis/_user-response-body.mdx
+++ b/astro/src/content/docs/apis/_user-response-body.mdx
@@ -5,38 +5,18 @@ import JSON from 'src/components/JSON.astro';
import ModerationStatusResponse from 'src/content/docs/apis/_moderation_status_response.mdx';
import RemovedSince from 'src/components/api/RemovedSince.astro';
import UserDataEmailFieldResponse from 'src/content/docs/apis/_user-data-email-field-response.mdx';
+import EmailVerificationIdsFieldsResponse from 'src/content/docs/apis/_user-email-verification-ids-response.mdx';
+import RegistrationVerificationIdsFieldsResponse from 'src/content/docs/apis/_user-registration-verification-ids-response.mdx';
+import UserMembershipFieldsResponse from 'src/content/docs/apis/_user-memberships-response.mdx';
+import UserTokenFieldsResponse from 'src/content/docs/apis/_user-token-response.mdx';
+import UserLoginFieldsResponse from 'src/content/docs/apis/_user-login-response.mdx';
#### Response Body
- { (props.login_response || props.passwordless_login_response) && <>
-
-
- The pending identity provider link Id. This value is created when logging in with an identity provider configured with a linking strategy of Create a pending link. It will only be included in the response body when this strategy is configured and a link does not yet exist for the user. It is used in conjunction with the Link APIs to complete a pending link.
-
-
-
- The refresh token that can be used to obtain a new access token once the provided one has expired.
-
- Because a refresh token is per user and per application, this value will only be returned when an applicationId was provided on the login request and the user is registered to the application.
-
- You must explicitly allow generation of refresh tokens when using the Login API. Configure the application.loginConfiguration.generateRefreshTokens setting via the API or enable the setting by navigating to the Application -> My Application -> Security tab.
-
-
-
- When the refreshToken is returned in the response, this field will also be returned. This unique Id is the persistent identifier for this refresh token, and will not change even when using one-time use refresh tokens. This value may optionally be used to revoke the token using the Refresh Token API.
-
-
- If authenticated using a One Time Password and state was provided during the Change Password request this value will be returned exactly as it was provided.
-
-
- If authenticated using Two Factor and state was provided during the Two Factor Start request this value will be returned exactly as it was provided.
-
-
- If state was provided during the passwordless login send request this value will be returned exactly as it was provided.
-
-
- > }
+ { (props.login_response || props.passwordless_login_response) &&
+
+ }
A trust token that may be used to complete another API request that requires trust. For example, if you receive an error from an API indicating trust is required - indicated by this error code `[TrustTokenRequired]`, this value can be used to satisfy the trust requirement.
@@ -46,38 +26,17 @@ import UserDataEmailFieldResponse from 'src/content/docs/apis/_user-data-email-f
subsequent login requests to bypass the Two Factor challenge.
- { (props.create_user || props.login_response || props.passwordless_login_response || props.idp_response) && <>
-
-
- The access token, this string is an encoded JSON Web Token (JWT).
-
-
- The instant the token will expire. If the response does not contain a token, this field will also be omitted from the response.
-
-
- >}
-
- { (props.create_user || props.update_user || props.retrieve_user) && <>
-
-
- An email verification Id. When present, this will represent the user's current email verification Id.
-
- When using FormField verification strategy, this is the first part of the pair of verification Ids, and the emailVerificationOneTimeCode is the second part. When ClickableLink is the verification strategy, this value is sensitive in that this value by itself can be used to verify the user's email address.
-
- When ClickableLink is the verification strategy, you may optionally use this value to use an out of band transport such as your own email service.
-
- Prior to version 1.49.0, this value was only returned when using FormField verification strategy. Beginning in 1.49.0 the value is always returned when available.
-
-
-
- An email one time code that will be paired with the emailVerificationId.
-
- When FormField is the verification strategy, you may optionally use this value to use an out of band transport such as your own email service.
+ { (props.create_user || props.login_response || props.passwordless_login_response || props.idp_response) &&
+
+ }
- This value will only be present when using the FormField verification strategy. When present, this is the value the user will need to enter to complete email verification.
-
+ { (props.create_user || props.update_user || props.retrieve_user) &&
+
+ }
- >}
+ { props.retrieve_user &&
+
+ }
True if the User is active. False if the User has been deactivated. Deactivated Users will not be able to login.
@@ -88,11 +47,9 @@ import UserDataEmailFieldResponse from 'src/content/docs/apis/_user-data-email-f
The [instant](/docs/reference/data-types#instants) this user's password was last checked to determine if it is compromised.
- { (!props.connector_response) && <>
The unique Id of the [Connector](/docs/lifecycle/migrate-users/connectors/) associated with the System of Record being used to authenticate the user.
- >}
This Id is used by FusionAuth when the User's username is sent to CleanSpeak to be moderated (filtered and potentially sent to the approval queue). It is the **content Id** of the username inside CleanSpeak.
@@ -136,25 +93,9 @@ import UserDataEmailFieldResponse from 'src/content/docs/apis/_user-data-email-f
The [instant](/docs/reference/data-types#instants) when the User was last updated.
- {!props.create_user && <>
-
-
- The list of memberships for the User.
-
-
- An object that can hold any information about the User for this membership that should be persisted.
-
-
- The Id of the Group of this membership.
-
-
- The unique Id of this membership.
-
-
- The instant that the membership was created.
-
-
- >}
+ {!props.create_user &&
+
+ }
The User's middle name.
diff --git a/astro/src/content/docs/apis/_user-token-response.mdx b/astro/src/content/docs/apis/_user-token-response.mdx
new file mode 100644
index 0000000000..2394d1db4a
--- /dev/null
+++ b/astro/src/content/docs/apis/_user-token-response.mdx
@@ -0,0 +1,9 @@
+import APIField from 'src/components/api/APIField.astro';
+import InlineField from 'src/components/InlineField.astro';
+
+
+ The access token, this string is an encoded JSON Web Token (JWT).
+
+
+ The instant the token will expire. If the response does not contain a token, this field will also be omitted from the response.
+
diff --git a/astro/src/content/docs/apis/authentication.mdx b/astro/src/content/docs/apis/authentication.mdx
index edcea25b3b..6ea5c4dc11 100644
--- a/astro/src/content/docs/apis/authentication.mdx
+++ b/astro/src/content/docs/apis/authentication.mdx
@@ -7,6 +7,7 @@ topOfNav: true
import APIAuthenticationIcon from "src/components/api/APIAuthenticationIcon.astro";
import APIBlock from 'src/components/api/APIBlock.astro';
import APIField from 'src/components/api/APIField.astro';
+import APIKeyCrossTenantNote from 'src/content/docs/apis/_api-key-cross-tenant-note.mdx';
import Aside from 'src/components/Aside.astro';
import ClientSideApiKeys from 'src/content/docs/_shared/_client-side-api-keys.mdx';
import NewApiKey401 from 'src/content/docs/apis/_new-api-key-401.mdx';
@@ -192,6 +193,8 @@ Prior to version `1.26.0`, the FusionAuth administrative user interface was the
The optional tenant to which this API key will be assigned. This value cannot be changed once the API key is created.
When you assign an API key to a tenant, any requests made with this key will only be able to operate on users, applications, groups, and other entities in the selected tenant.
+
+
One or more endpoints this API key will be authorized to access.
diff --git a/astro/src/content/docs/apis/users.mdx b/astro/src/content/docs/apis/users.mdx
index 69d07d08dc..b64abff103 100644
--- a/astro/src/content/docs/apis/users.mdx
+++ b/astro/src/content/docs/apis/users.mdx
@@ -572,37 +572,37 @@ The response will contain recent logins containing no more than the value set by
The city where the login request originated.
-
+
The country where the login request originated.
-
+
The latitude where the login request originated.
-
+
The longitude where the login request originated.
-
+
The geographic location where the login request originated.
-
+
The zipcode where the login request originated.
-
+
@@ -734,7 +734,7 @@ When authenticated using an API key a response body will be provided. If an API
Depending on your tenant configuration, this may be returned. The verification One Time Code is used with the gated Email Verification workflow. The user enters this code to verify their email.
-
+
diff --git a/astro/src/content/docs/customize/look-and-feel/_theme-upgrade.mdx b/astro/src/content/docs/customize/look-and-feel/_theme-upgrade.mdx
index 4cfa1648b4..c6911bb3f9 100644
--- a/astro/src/content/docs/customize/look-and-feel/_theme-upgrade.mdx
+++ b/astro/src/content/docs/customize/look-and-feel/_theme-upgrade.mdx
@@ -1,6 +1,16 @@
import InlineField from 'src/components/InlineField.astro';
import InlineUIElement from 'src/components/InlineUIElement.astro';
+As you upgrade your FusionAuth version, you'll also want to make sure you test and possibly upgrade your theme.
+
+### CSS
+
+If you can meet your look and feel customization needs by only modifying the CSS stylesheet, rather than the theme templates, upgrades to later versions of FusionAuth will be easier.
+
+When you limit yourself to making CSS changes, you'll still need to review the release notes and verify that any newly introduced pages look good, but you won't have to propagate customized template changes.
+
+However, if you need to make template changes, no worries, FusionAuth supports that use case as well.
+
### Templates
When new functionality is introduced to [the hosted login pages](/docs/get-started/core-concepts/integration-points#hosted-login-pages), new theme templates are occasionally added. They are added to the default theme by the upgrade process, but if you've customized your theme to fit your brand, you'll need to modify the theme to have the new template.
diff --git a/astro/src/content/docs/customize/look-and-feel/index.mdx b/astro/src/content/docs/customize/look-and-feel/index.mdx
index ad99af6478..458e7e62b6 100644
--- a/astro/src/content/docs/customize/look-and-feel/index.mdx
+++ b/astro/src/content/docs/customize/look-and-feel/index.mdx
@@ -181,6 +181,18 @@ When building a theme, the [FusionAuth theme helper project](https://github.com/
You can pull down all your templates, edit them locally, and have them transparently uploaded to your FusionAuth instance.
+### Managing Many Themes
+
+If you have a large number of themes, you'll want additional tooling to manage them. Best practices include:
+
+* Put your themes under version control and use CI/CD and one of the [client libraries](/docs/sdks) to apply changes.
+* Prefer modifying CSS rather than theme templates.
+* Leverage `tenant.data` for a small number of attributes that differ between tenants, which allows you to use the same theme with modified templates. See [Environment Management](#environment-management) for an example.
+* Consider generating your themes locally using a templating language such as jinja and then uploading them.
+* Automatically assign themes to tenants, using one of the [client libraries](/docs/sdks).
+
+There is an [open feature request](https://github.com/FusionAuth/fusionauth-issues/issues/1869) to allow for theme inheritance, but it is not currently on the roadmap.
+
## Environment Management
diff --git a/astro/src/content/docs/extend/events-and-webhooks/kafka/index.mdx b/astro/src/content/docs/extend/events-and-webhooks/kafka/index.mdx
index 0b1af9bffc..8b7f3a4e35 100644
--- a/astro/src/content/docs/extend/events-and-webhooks/kafka/index.mdx
+++ b/astro/src/content/docs/extend/events-and-webhooks/kafka/index.mdx
@@ -8,6 +8,7 @@ tertcategory: kafka
# TODO: move kafka example out to a GH repo
---
import KafkaTroubleshooting from 'src/content/docs/extend/events-and-webhooks/kafka/_kafka_troubleshooting.mdx';
+import {RemoteCode} from '@fusionauth/astro-components';
## Overview
@@ -39,119 +40,7 @@ If you're running Kafka alongside FusionAuth (for example, from the same `docker
If your Docker Compose file is as follows:
-```
-version: '3'
-
-services:
- db:
- image: postgres:12.9
- environment:
- PGDATA: /var/lib/postgresql/data/pgdata
- POSTGRES_USER: ${POSTGRES_USER}
- POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
- healthcheck:
- test: [ "CMD-SHELL", "pg_isready -U postgres" ]
- interval: 5s
- timeout: 5s
- retries: 5
- networks:
- - db_net
- restart: unless-stopped
- volumes:
- - db_data:/var/lib/postgresql/data
-
- search:
- image: docker.elastic.co/elasticsearch/elasticsearch:7.17.0
- container_name: search
- environment:
- cluster.name: fusionauth
- bootstrap.memory_lock: "true"
- discovery.type: single-node
- ES_JAVA_OPTS: ${ES_JAVA_OPTS}
- healthcheck:
- test: [ "CMD", "curl", "--fail" ,"--write-out", "'HTTP %{http_code}'", "--silent", "--output", "/dev/null", "http://localhost:9200/" ]
- interval: 5s
- timeout: 5s
- retries: 5
- networks:
- - search_net
- restart: unless-stopped
- ulimits:
- memlock:
- soft: -1
- hard: -1
- volumes:
- - search_data:/usr/share/elasticsearch/data
-
- fusionauth:
- image: fusionauth/fusionauth-app:latest
- depends_on:
- - db
- - search
- - kafka
- - zookeeper
- environment:
- DATABASE_URL: jdbc:postgresql://db:5432/fusionauth
- DATABASE_ROOT_USERNAME: ${POSTGRES_USER}
- DATABASE_ROOT_PASSWORD: ${POSTGRES_PASSWORD}
- DATABASE_USERNAME: ${DATABASE_USERNAME}
- DATABASE_PASSWORD: ${DATABASE_PASSWORD}
- FUSIONAUTH_APP_MEMORY: ${FUSIONAUTH_APP_MEMORY}
- FUSIONAUTH_APP_RUNTIME_MODE: development
- FUSIONAUTH_APP_URL: http://fusionauth:9011
- SEARCH_SERVERS: http://search:9200
- SEARCH_TYPE: elasticsearch
-
- networks:
- - db_net
- - search_net
- restart: unless-stopped
- ports:
- - 9011:9011
- volumes:
- - fusionauth_config:/usr/local/fusionauth/config
-
- zookeeper:
- image: confluentinc/cp-zookeeper:latest
- environment:
- ZOOKEEPER_CLIENT_PORT: 2181
- ZOOKEEPER_TICK_TIME: 2000
- ports:
- - 2181:2181
- networks:
- - db_net
-
- kafka:
- image: confluentinc/cp-kafka:latest
- depends_on:
- - zookeeper
- ports:
- - 9092:9092
- environment:
- KAFKA_BROKER_ID: 1
- KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
- KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092
- KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT
- KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
- KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
- networks:
- db_net:
- aliases:
- - kafka
-
-networks:
- db_net:
- driver: bridge
- search_net:
- driver: bridge
-
-volumes:
- db_data:
- fusionauth_config:
- search_data:
-
-```
-
+
Then you would input the following configuration in the FusionAuth UI to configure the Kafka integration.
diff --git a/astro/src/content/docs/get-started/core-concepts/roles.mdx b/astro/src/content/docs/get-started/core-concepts/roles.mdx
index 0a78662f21..4f3632091b 100644
--- a/astro/src/content/docs/get-started/core-concepts/roles.mdx
+++ b/astro/src/content/docs/get-started/core-concepts/roles.mdx
@@ -149,7 +149,7 @@ _FusionAuth user_support_manager_
| user | Edit a user, except for any identity information that could be used to authenticate. For example, the email and username cannot be modified. |
| user | Lock a user account. |
| user | Unlock a user account. |
-| user | Modify 2FA settings if available. |
+| user | View 2FA settings if available. |
| user | Action a user. |
| user | Add a comment to a user. |
| user | Verify a user's email address. |
diff --git a/astro/src/content/docs/get-started/run-in-the-cloud/cloud.mdx b/astro/src/content/docs/get-started/run-in-the-cloud/cloud.mdx
index 59e62e2c48..62cd245e1a 100644
--- a/astro/src/content/docs/get-started/run-in-the-cloud/cloud.mdx
+++ b/astro/src/content/docs/get-started/run-in-the-cloud/cloud.mdx
@@ -534,7 +534,7 @@ You may notice FusionAuth Cloud now supports a `[uuid].durable.fusionauth.io` CN
### Unlimited Custom Domains
-
+
FusionAuth Cloud supports unlimited custom domains for Enterprise customers with High Availability deployments. Aside from allowing unlimited custom domains, the feature also has the following benefits:
@@ -822,5 +822,6 @@ FusionAuth Cloud has the same [limitations](/docs/get-started/core-concepts/limi
* If you are on FusionAuth Cloud and you find that some requests are failing, it is possible you are being rate limited. This isn't intentional, but an automated part of our infrastructure to ensure FusionAuth Cloud performance and security. If you are rate limited but need these requests to occur, please open a [support ticket](https://account.fusionauth.io/account/support/) with details.
* If you want to run Advanced Threat Detection, an Enterprise feature, you'll need a cloud deployment with sufficient memory. Currently that means it must be a Large or X-Large size.
* With HA and other multi-node deployments, requests are passed through a load balancer. When making requests to node specific metrics endpoints such as `/api/status` or `/api/prometheus/metrics` each request may return different results because the response is specific to the service node responding to the request.
-* If you are using the Elasticsearch search engine, you may not modify the Elasticsearch settings or view the Elasticsearch index directly. Among other things, this means that you can't use some of the [troubleshooting steps](/docs/operate/troubleshooting/troubleshooting) available to users self-hosting FusionAuth.
+* You may not modify the Elasticsearch settings or view the Elasticsearch index directly. Among other things, this means that you can't use some of the [troubleshooting steps](/docs/operate/troubleshooting/troubleshooting) available to users self-hosting FusionAuth.
* OpenTelemetry data is not available on FusionAuth Cloud deployments.
+* There is a limit of 1000 indexed fields. These include `user.data`, `registration.data` and standard indexed fields like `email`.
diff --git a/astro/src/content/docs/get-started/run-in-the-cloud/github-actions.mdx b/astro/src/content/docs/get-started/run-in-the-cloud/github-actions.mdx
index 1a523f28ff..17c090a98c 100644
--- a/astro/src/content/docs/get-started/run-in-the-cloud/github-actions.mdx
+++ b/astro/src/content/docs/get-started/run-in-the-cloud/github-actions.mdx
@@ -44,7 +44,7 @@ You might have the following questions:
Automating these tasks is called CI/CD or continuous integration and continuous deployment.
- CI refers to testing changes to code you push to the main branch to ensure it fits in with the existing system and everything still works.
-- CD refers to deploying every push to the main branch to your production (live) site.
+- CD refers to deploying every push to the main branch of your production (live) site.
GitHub provides a tool for automating workflows called GitHub Actions. It provides a virtual machine in which you can run scripts to check out your code, compile it, deploy alongside FusionAuth and other services, run tests, and manage deployment.
@@ -54,11 +54,11 @@ The flow looks like the diagram below.
## A Simple Example Using GitHub Actions
-There is a minimal but complete CI/CD example in this repository. An overview of the set-up is shown below.
+There is a minimal but complete CI/CD example in this repository. An overview of the setup is shown below.
-In GitHub, click Fork and add the repository to your GitHub account. Then run `git clone https://github.com//fusionauth-example-github-actions.git` in a terminal to download it.
+In GitHub, click Fork and add the repository to your GitHub account. Then run `git clone https://github.com//fusionauth-example-github-actions.git` in a terminal to download it. Remember to replace `` with your GitHub username.
Below is the repository structure.
@@ -151,7 +151,7 @@ fa | 12:49:04.501 PM INFO JDBCMaintenanceModeDatabaseService - Unlock completed
If you browse to FusionAuth again, you'll see that the user you created is still there.
-The FusionAuth application is separate from the FusionAuth database. In this case, the database is stored in a Docker volume. When the upgrade scripts run, the data should not be broken, and upgrading FusionAuth should be unnoticeable. However, mistakes can happen, as well as database crashes, and you should backup both the FusionAuth database and your application database daily. The rest of this article will show you how to automate login tests to check that your system still works after upgrading FusionAuth or your app. If something breaks, you can restore the old version of the database and begin debugging.
+The FusionAuth application is separate from the FusionAuth database. In this case, the database is stored in a Docker volume. When the upgrade scripts run, the data should not be broken, and upgrading FusionAuth should be unnoticeable. However, mistakes can happen, as well as database crashes, and you should back up both the FusionAuth database and your application database daily. The rest of this article will show you how to automate login tests to check that your system still works after upgrading FusionAuth or your app. If something breaks, you can restore the old version of the database and begin debugging.
For more details on upgrading FusionAuth, including non-silent upgrades, please read this [article](/docs/get-started/download-and-install/docker#upgrading).
@@ -232,7 +232,7 @@ Now that you know how to test your app after a change to it or FusionAuth, let's
By default, GitHub prevents action workflows from running on forked repositories. To grant permission for the workflow to execute, navigate to the Actions tab on your forked repository and click on the I understand my workflows, go ahead and enable them button on the repository.
-In this example we'll trigger the execution of the test by listening to push events on the main branch. Open the file `.github/workflows/test.yaml` in your IDE. The fifth line in the file specifies the branch name that triggers the execution of the action when commits are pushed to it. It currently reads as follows.
+In this example, we'll trigger the execution of the test by listening to push events on the main branch. Open the file `.github/workflows/test.yaml` in your IDE. The fifth line in the file specifies the branch name that triggers the execution of the action when commits are pushed to it. It currently reads as follows.
```yaml
- main_RENAME_THIS_TO_ENABLE_TEST
@@ -260,20 +260,20 @@ If your test runs successfully, you can be certain that your app and FusionAuth
GitHub Actions is a service provided by GitHub that allows you to run workflows that can do anything you can write in a script, triggered by different types of events in your repository. GitHub runs your script in a virtual machine. GitHub allocates each user a certain amount of free CPU minutes per month and a set amount of storage, depending on your plan. For more details, see the [learn GitHub Actions](https://docs.github.com/en/actions/learn-github-actions) documentation and [pricing page](https://docs.github.com/en/billing/managing-billing-for-github-actions/about-billing-for-github-actions).
-Let's review the action script to see how it works. All scripts are stored in the `.github/workflows` directory, and the files can be called whatever you like. Here, the file is called `test.yaml`.
+Let's review the GitHub Actions workflow script to see how it works. All scripts are stored in the `.github/workflows` directory, and the files can be called whatever you like. Here, the file is called `test.yaml`.
-Each action has a name, which is a human-readable label.
+Each workflow has a name, which is a human-readable label.
```yaml
name: Test FusionAuth login
```
-This is followed by an event, which is all the conditions that trigger the action to execute. Events are stored in the `on` object. The most common ones to use are when pushing a commit or receiving a pull request. For a full list, see the [documentation](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows).
+This is followed by an event, which is all the conditions that trigger the workflow to execute. Events are stored in the `on` object. The most common ones to use are when pushing a commit or receiving a pull request. For a full list, see the [documentation](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows).
-Your action uses the push-to-main-branch event.
+Your workflow uses the push-to-main-branch event.
```yaml
push:
@@ -281,51 +281,39 @@ Your action uses the push-to-main-branch event.
- main
```
-Then comes the action itself, which is stored in the `jobs` object. This is what will run when the event occurs. Your action has just one job, `run-tests`.
+Then comes the workflow `jobs`. This is what will run when the push event occurs. This workflow has just one job, `run-tests`.
-The `services` section specifies that the action requires Docker (where FusionAuth runs).
-
-```yaml
- services:
- docker:
- image: docker:19.03.12
- options: --privileged # container has full access to host
- ports:
- - 3000:3000
- - 9011:9011
-```
+The `steps` section defines a sequence of tasks that are executed during the workflow run.
The steps consist of two types of code:
- Actions, which are existing tasks in GitHub like checking out a repository or installing Node.js.
- Custom commands, which run code directly in the machine's terminal.
-
-
-This script starts by checking out your repository and installing Node.js in actions.
+This script starts by checking out your repository, and then installs FusionAuth and Node.js using the respective actions.
```yaml
steps:
- name: Checkout repository
uses: actions/checkout@v4
+ - name: Start FusionAuth
+ uses: fusionauth/fusionauth-github-action@v1
+ with:
+ FUSIONAUTH_VERSION: "latest" # Optional: provide FusionAuth version number otherwise it defaults to latest
+
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
```
-Custom code then starts Docker, installs Node.js modules, runs the app, and runs the tests. Note that steps usually run in serial. To run steps simultaneously, you use `-d` in Docker, and `npm run start &` to start the next step without waiting for the first step to exit. If a step fails, the next step will not run.
+Custom code then installs Node.js modules, runs the app, and runs the tests. Note that steps usually run in serial. To run steps simultaneously, you use `&` to start the next step without waiting for the first step to exit. If a step fails, the next step will not run.
```yaml
- - name: Start FusionAuth in Docker
- run: docker-compose up -d # -d in background
-
- name: Install npm dependencies
run: |
npm install
@@ -342,9 +330,30 @@ Custom code then starts Docker, installs Node.js modules, runs the app, and runs
working-directory: ./app
```
+Using the `fusionauth-github-action` is the preferred method to start FusionAuth in the workflow. If you want to start FusionAuth with Docker compose instead of the action, you can use the commented-out configuration in the `.github/workflows/test.yaml` file (for example, if you need customizations to use a custom password hashing plugin).
+
+In the commented-out workflow configuration, the `services` section specifies that the action requires Docker where FusionAuth will run.
+
+```yaml
+ services:
+ docker:
+ image: docker:19.03.12
+ options: --privileged # container has full access to host
+ ports:
+ - 3000:3000
+ - 9011:9011
+```
+
+Then FusionAuth is started using `docker-compose`.
+
+```yaml
+ - name: Start FusionAuth in Docker
+ run: docker-compose up -d # -d in background
+```
+
## Compile Your App In A GitHub Action
-You might have noticed that GitHub did not build your app at any point in this workflow. This is because JavaScript is a dynamic language and does not need to be compiled. If you are using FusionAuth with C#, Go, Rust, or TypeScript, you can easily add another step to your action to compile the code before running the tests. This might look like the code below.
+You might have noticed that GitHub did not build your app at any point in this workflow. This is because JavaScript is a dynamic language and does not need to be compiled. If you are using FusionAuth with C#, Go, Rust, or TypeScript, you can easily add another step to your workflow to compile the code before running the tests. This might look like the code below.
```yaml
- name: Compile app
@@ -377,7 +386,7 @@ We'll discuss the secrets used in the code below in a later section
+The API properties and Button properties will only be applied when using the [Google Identity Services API](https://developers.google.com/identity/gsi/web/reference/html-reference). When using a Login Method of “Use redirect for login” the GIS API will not be used and the properties will not be used. The various properties of the button can be set using [Themes](/docs/customize/look-and-feel/) in this case.
+
+
#### Form Fields
diff --git a/astro/src/content/docs/lifecycle/authenticate-users/passwordless/webauthn.mdx b/astro/src/content/docs/lifecycle/authenticate-users/passwordless/webauthn.mdx
index f8746036b9..d4dd9fe9f5 100644
--- a/astro/src/content/docs/lifecycle/authenticate-users/passwordless/webauthn.mdx
+++ b/astro/src/content/docs/lifecycle/authenticate-users/passwordless/webauthn.mdx
@@ -36,7 +36,7 @@ WebAuthn is a W3C [specification](https://www.w3.org/TR/webauthn-2/) that define
You can enable WebAuthn in your FusionAuth instance with a few configuration changes. This guide explains these configuration options and provides detail to help you choose the best options for your instance.
-
+
See the [Licensing](/docs/get-started/core-concepts/licensing) guide for information about activating a license for your FusionAuth instance.
@@ -134,7 +134,7 @@ The workflow settings allow limiting the associated workflow to one attachment o
* Bootstrap - ``Any``. The bootstrap workflow can be used to authenticate on any supported device. The `Any` option allows signing in with WebAuthn across the broadest set of devices.
* Re-authentication - ``Platform only``. In order to ensure that the authenticator is available for repeated logins on the same device, it is best to limit authenticator selection to those integrated with the client device.
-
+
#### User verification
diff --git a/astro/src/content/docs/lifecycle/migrate-users/connectors/generic-connector.mdx b/astro/src/content/docs/lifecycle/migrate-users/connectors/generic-connector.mdx
index 63969514ae..d421d56396 100644
--- a/astro/src/content/docs/lifecycle/migrate-users/connectors/generic-connector.mdx
+++ b/astro/src/content/docs/lifecycle/migrate-users/connectors/generic-connector.mdx
@@ -63,7 +63,7 @@ The security settings may be used to require authentication in order to make the
The password to be used for HTTP Basic Authentication.
- The SSL certificate to be used when connecting to the POST endpoint.
+ The SSL certificate to be used when connecting to the POST endpoint.
If you need to add a certificate for use with this connector, navigate to Settings -> Key Master and import a certificate. The certificate will then be shown as an option in this form control.
@@ -121,7 +121,7 @@ If the user cannot be authenticated, then you should return a `404`.
-
+
@@ -156,7 +156,7 @@ For Connect timeout, if the timeout expires before th
For Read timeout, if the timeout expires before there is data available to be read, the login fails.
The default values for these timeouts are typically adequate, but you may want to tune them if there is resource contention on your server.
-However, neither timeout prevents the HTTP request from taking as long as it needs to in order to respond once the API endpoint has begun to write bytes to the output stream.
+However, neither timeout prevents the HTTP request from taking as long as it needs to in order to respond once the API endpoint has begun to write bytes to the output stream.
If a Connector is not correctly terminating the HTTP response or taking an excessive time to process the login request, then this can cause issues under load.
You can avoid issues by having your API endpoint write login data and completely close the connection as quickly as possible.
diff --git a/astro/src/content/docs/lifecycle/migrate-users/general-migration.mdx b/astro/src/content/docs/lifecycle/migrate-users/general-migration.mdx
index 852e4a313b..8221c32a73 100644
--- a/astro/src/content/docs/lifecycle/migrate-users/general-migration.mdx
+++ b/astro/src/content/docs/lifecycle/migrate-users/general-migration.mdx
@@ -20,15 +20,15 @@ import InlineUIElement from 'src/components/InlineUIElement.astro';
This guide will help you migrate existing users into FusionAuth. It covers the types of migrations available as well as the phases and activities of a successful user migration.
-## Types of Migrations
+## Types Of Migrations
-There are three approaches to user data migration. Every migration involves a cutover for a user, where they authenticate with the new system and not with the old one. While each approach differs in implementation details, a good way to consider which is right for you is to look at how many cutovers you want to handle.
+There are three approaches to user data migration. Every migration involves transferring data from the old system to the new system, called backfill, followed by a cutover for a user, where they authenticate with the new system and not with the old one. While each approach differs in implementation details, a good way to consider which is right for you is to look at how many cutovers you want to handle.
-You can:
+You can:
-* Migrate everyone at once, also known as a "big bang" migration. With this approach, you have one cutover.
+* Migrate everyone at once, also known as a "big bang" migration or "offline migration". With this approach, you have one cutover.
* Segment your users and migrate each segment. With this method, you have multiple cutovers, each with a natural chunk of users.
-* Migrate when a user authenticates, also known as a "slow migration". With this choice, there are two cutover points. The first is the application cutover, which happens when you direct users to FusionAuth for authentication. Then, at each user's login, the data is migrated and the user's system of record changes. Therefore there are many data cutover events.
+* Migrate when a user authenticates, also known as a "slow migration", "rolling migration", or "online migration". With this choice, there are two cutover points. The first is the application cutover, which happens when you direct users to FusionAuth for authentication. Then, at each user's login, the data is migrated and the user's system of record changes. Therefore there are many data cutover events.
Each of these approaches migrates user and other account data into FusionAuth from one or more other systems of record. All three options are supported by FusionAuth; pick the one which works best for your situation. Let's examine each approach in more detail.
@@ -36,8 +36,8 @@ Each of these approaches migrates user and other account data into FusionAuth fr
With a big bang migration, you are moving all your users at one time. The exact duration varies, but there is a single cutover period. The basic steps are:
-* Map user attributes from the old system to the new system.
-* Build a set of migration scripts or programs.
+* Map user attributes from the old system to the new system.
+* Build a set of migration scripts or programs.
* Test it well. Ensure that migration accuracy and duration meet your needs.
* Plan to modify your applications to point to the new system.
* When you are ready to migrate, bring your systems down or to a mode where authentication is degraded (read-only or disallowed).
@@ -46,22 +46,23 @@ With a big bang migration, you are moving all your users at one time. The exact
This approach has strengths:
-* If you manage the timing of auth unavailability, the migration can have minimal impact on users.
+* If you manage the timing of auth unavailability, the migration can have minimal impact on users.
* It has a fixed timeframe. When you have completed the migration, you're done and can shortly shut down the original system.
* If you have to decommission the old system by a certain deadline, perhaps due to an upcoming license renewal or other external factors, you can plan to migrate before the deadline.
* You only have to run two production user auth systems for a short period of time; typically you'll run the original system after the cutover in case you need to roll back.
* Employees or contractors accessing user data, such as customer service reps, only need to switch their working routines after the migration is performed.
-The big bang approach has some challenges, though.
+The big bang approach has some challenges, though.
-* It is common to miss issues during testing because this is a unique procedure. Production systems are often different in subtle ways from testing environments.
+* It is common to miss issues during testing because this is a unique procedure. Production systems are often different in subtle ways from testing environments.
* Any problems with the migration impact many users, since all are migrated.
* The big bang requires you to write code which you'll test intensely, use once and then throw away.
* The new auth system must be compatible with the old system's password hashing algorithm for the migration to be transparent to the end user. (An alternative is to force all users to reset their password.)
+* New users may not register during migration, nor may users alter their data, or the changes will not be available in the new system. This means migration has to be as fast as possible. Alternatively, you might allow users to continue using the old system during migration, then perform a final synchronization of data once migration is complete and cutover has happened.
In short, this is a high risk, low outage duration, high reward solution.
-### Segment by Segment Migration
+### Segment By Segment Migration
Segment by segment migration is the second alternative. It can be thought of as a series of "little bang" migrations. With this approach, you split your user accounts into segments and migrate each segment. Natural division points could be the type of user, source of user data, or applications used.
@@ -75,16 +76,16 @@ However, this approach is not without its issues:
* This will take longer to complete, requiring you to run both old and new systems for longer.
* You'll need to consider how to handle the cutover from the old system to the new system. Depending on how you segment your users, this could be complicated and require additional development. For example, if you divide your users by type and migrate the admin user segment first, you will need some kind of proxy in front of your auth systems to send admin users to the new system and normal users to the old one.
-Segment by segment migration decreases cutover risk, but in exchange requires a longer cutover timeline.
+Segment by segment migration decreases cutover risk, but in exchange requires a longer cutover timeline.
-### Slow Migration
+### User By User (Slow) Migration
-This approach is a logical extension of segment by segment migration. Here, each segment is a single user. With a slow migration:
+This approach is a logical extension of segment by segment migration. Here, each segment is a single user. With a slow migration:
-* Map user attributes from the old system to the new system.
+* Map user attributes from the old system to the new system.
* Set up a connection between the original auth system and FusionAuth.
* Modify your application or applications to point to FusionAuth. This is the application cutover point, which may require some downtime.
-* FusionAuth receives all auth requests, but delegates the first such request for each user to the original user management system.
+* FusionAuth receives all auth requests, but delegates the first such request for each user to the original user management system.
* The old system returns the information and FusionAuth creates a new user. This is the data "cutover" point for this user.
* For this user's subsequent authentication requests, FusionAuth is now the system of record. The user has been migrated.
@@ -93,19 +94,19 @@ To implement a slow migration, FusionAuth needs to pass the user's auth credenti
* Since you are only doing a user migration at the time a user authenticates, the blast radius of a mistake is smaller; it's limited to whoever is logging in.
* You can upgrade your password hash algorithms transparently without requiring anyone to reset their password. FusionAuth supports a [number of different algorithms](/docs/reference/password-hashes) and you can also [bring your own](/docs/extend/code/password-hashes/writing-a-plugin) as well.
* You don't have to migrate inactive users; this lets you scrub your user base.
-* You can use this opportunity to contact any dormant application users and encourage them to log in.
+* You can use this opportunity to contact any dormant application users and encourage them to log in.
* There's less downtime during the application cutover because you aren't moving any data, only switching where users authenticate.
-* You don't have to understand all the moving pieces of the old auth system. You don't have to understand all the business logic which goes into authentication in the old system.
+* You don't have to understand all the moving pieces of the old auth system. You don't have to understand all the business logic which goes into authentication in the old system.
However, a slow migration isn't the right solution for every application. Issues to be aware of:
* You are passing a user's plaintext password from FusionAuth to the old auth system. Take special care to secure this data in transit. If possible, keep it from traveling over the internet.
-* The old user management solution must be extensible or support a standard like LDAP. You may need to extend it to add an auth API and you need to understand the user account attributes.
+* The old user management solution must be extensible or support a standard like LDAP. You may need to extend it to add an auth API and you need to understand the user account attributes.
* You have to run both FusionAuth and the original system for the duration of the migration. Depending on the state of the old user auth management software, this may be painful.
* Customer service and other internal users may need to access two systems to find a user during the migration period.
* Rollback from a phased migration is more complex if there are issues, because there are two systems of record, one for migrated users and one for users in the old system.
-A slow migration is, in short, a lower risk, long duration choice.
+A slow migration is, in short, a lower risk, long duration choice.
## Migration Implementation
@@ -119,16 +120,16 @@ The [Core Concepts section](/docs/get-started/core-concepts/) is worth reviewing
* A tenant is a top level object that contains users, applications and groups.
* Applications have roles. Users authenticate and are authorized to access applications.
-* Groups contain users and may have associated roles.
+* Groups contain users and may have associated roles.
* Users have registrations with applications. You can create registrations at the same time you are creating a user.
All entities have a [UUID](/docs/reference/data-types#uuids) identifier. This Id can be specified on creation, but must be a valid UUID. If you have an identifier that is not a valid UUID, one option is to store the old Id in the `data` field of the FusionAuth configuration.
-#### Evaluating FusionAuth
+#### Evaluating FusionAuth
-If you haven't already done so, ensure FusionAuth will work with your application or applications. You can [install it in about five minutes](/docs/quickstarts/5-minute-setup-guide) and build out a proof of concept (POC).
+If you haven't already done so, ensure FusionAuth will work with your application or applications. You can [install it in about five minutes](/docs/quickstarts/5-minute-setup-guide) and build a prototype.
-A POC is helpful in determining which [login method](/docs/get-started/core-concepts/integration-points#login-options) you should use and how to [theme the hosted login pages](/docs/customize/look-and-feel/) to maintain your application's look and feel.
+A protoype is helpful in determining which [login method](/docs/get-started/core-concepts/integration-points#login-options) you should use and how to [theme the hosted login pages](/docs/customize/look-and-feel/) to maintain your application's look and feel.
FusionAuth assigns users roles. A user's roles are available in API responses and in the [JWT (JSON Web Token)](/docs/lifecycle/authenticate-users/login-api/json-web-tokens) sent to client applications after successful user authentication. You may need to update your application to look at the `roles` claim to allow or disallow functionality within an application.
@@ -138,11 +139,11 @@ If you allow users to register with your application, modify your application to
If you want social sign-on, such as Google, or enterprise identity provider integration, such as SAML, configure and enable those providers as well.
-Testing FusionAuth's ability to integrate with your identity provides and existing applications before diving into the migration planning will ensure that when the time comes to cut over to FusionAuth, there won't be any unpleasant surprises.
+Testing FusionAuth's ability to integrate with your identity provides and existing applications before diving into the migration planning will ensure that when the time comes to cut over to FusionAuth, there won't be any unpleasant surprises.
Next, let's talk about migration planning.
-### Migration Planning and Assessment
+### Migration Planning And Assessment
The first step to any successful data migration is planning, and user data migration is no different. You need to know:
@@ -150,23 +151,23 @@ The first step to any successful data migration is planning, and user data migra
* Who uses the data from each source
* If your application can work with the auth system in a read-only configuration
* How to connect to each datasource
-* What the user and account data looks like
+* What the user and account data looks like
* Special considerations such as SAML migration
-* Which migration approach fits your needs: big bang, segment by segment or slow migration
+* Which migration approach fits your needs: big bang, segment by segment or user by user migration
A full explanation of data migration planning is beyond the scope of this guide. But here are items to consider when moving user data.
-Think about the edge cases. What fields are required and optional in the old auth system or systems? FusionAuth requires minimal data about a user; only a password and username or email are required.
+Think about the edge cases. What fields are required and optional in the old auth system or systems? FusionAuth requires minimal data about a user; only a password and username or email are required.
-Is there a clean one-to-one mapping between the original system's auth fields and FusionAuth? The answer is usually "no". Therefore plan to spend some time examining the current system's data and seeing how it maps to FusionAuth's user schema, [as documented](/docs/apis/users). We'll look at an example of a mapping process in the next section.
+Is there a clean one-to-one mapping between the original system's auth fields and FusionAuth? The answer is usually "no". Therefore plan to spend some time examining the current system's data and seeing how it maps to FusionAuth's user schema, [as documented](/docs/apis/users). We'll look at an example of a mapping process in the next section.
What should you do if you have data which doesn't map cleanly to any of the fields available in FusionAuth? FusionAuth provides a `data` field on a number of entities, including the user entity and application registrations. This `data` field can be used to store arbitrary key value data, and is a good place to save any fields from the old system which don't map well to the FusionAuth user or application registration data models. In fact, it's often useful to store all the original user data in this field, so that you have it should you need it post-migration. Having access to the original, unmigrated data can be helpful in the future. If there was mistranslated data or fields, you'll be able to examine what was present in the old system without accessing it.
Consider how to handle unexpected data during the migration process. You can save off the record for further examination, toss it as malformed, or ignore only fields containing unexpected data. Which choice you make depends on your business needs and the value of each account.
-Don't forget to handle relationships between users and other identity-related entities. Groups, application associations, roles, historical data, and anything else from the old system which is tied to a user account. Find out where this data is coming from, if it should be migrated, and where it will end up. Such auxiliary data might be stored in FusionAuth or perhaps a different datastore is a better place.
+Don't forget to handle relationships between users and other identity-related entities. Groups, application associations, roles, historical data, and anything else from the old system which is tied to a user account. Find out where this data is coming from, if it should be migrated, and where it will end up. Such auxiliary data might be stored in FusionAuth or perhaps a different datastore is a better place.
-There are two common types of data involved in a user data migration which are worth closer examination.
+There are two common types of data involved in a user data migration which are worth closer examination.
The first is user ids. These identifiers are often referenced by other systems, including external ones, and may be used for auditing, analytics or other purposes. You can preserve these user ids in two ways when moving to FusionAuth.
@@ -175,9 +176,9 @@ The first is user ids. These identifiers are often referenced by other systems,
Next, consider a user's password, and related fields such as a salt or hashing scheme. Dealing with this data depends on your migration approach. For a big bang or segment by segment migration, ensure FusionAuth understands the original system's hashing algorithm [by writing a plugin](/docs/extend/code/password-hashes/custom-password-hashing) if the password was not hashed in one of [FusionAuth's supported algorithms](/docs/reference/password-hashes). For a slow migration, the password will be available for FusionAuth to hash or re-hash. If you have the user's passwords in plain text, FusionAuth can also hash them on import.
-If you have user data in multiple systems and are planning to merge the data, map the user fields from all the old system datastores.
+If you have user data in multiple systems and are planning to merge the data, map the user fields from all the old system datastores.
-#### An Example of Data Mapping
+#### An Example Of Data Mapping
Let's examine a data mapping example. Suppose an old auth system has the following user data model (let's ignore `email` and `password` fields, as they won't be necessarily be mapped):
@@ -220,7 +221,7 @@ As this example shows, getting ready for a migration consists of many choices an
### Setting Up FusionAuth
-Before you can migrate any user information into FusionAuth, ensure it is set up correctly. While you tested FusionAuth out previously, now it is time to set up a production ready instance.
+Before you can migrate any user information into FusionAuth, ensure it is set up correctly. While you tested FusionAuth out previously, now it is time to set up a production ready instance.
Determine where your FusionAuth instances should be hosted. You can self host in any data center or cloud provider, or use the managed services offering from FusionAuth, [FusionAuth Cloud](/pricing). Decide on whether you need a [support plan](/pricing), with guaranteed response times. Evaluate if you need any of the [paid plan features](/pricing).
@@ -234,7 +235,7 @@ Create one or more tenants. Multiple tenants are useful for allowing someone to
-Create one or many applications and add any roles needed for them. Create a FusionAuth application entity for each application whose users you are migrating. An application is anything a user can log in to, whether an API, a commercial product, or a custom web application. Add each of these via the API or navigating to Applications in the administrative user interface, then clicking the green plus sign to add the application.
+Create one or many applications and add any roles needed for them. Create a FusionAuth application entity for each application whose users you are migrating. An application is anything a user can log in to, whether an API, a commercial product, or a custom web application. Add each of these via the API or navigating to Applications in the administrative user interface, then clicking the green plus sign to add the application.
@@ -258,19 +259,19 @@ Disable the `user.bulk.create` webhook unless you need FusionAuth to send an eve
Set the HTTP timeout to a large value on your API requests. Exactly how to do this varies based on the tool you're using to make the HTTP request. The import API is currently a synchronous operation, though there are plans to make it asynchronous (see [this GitHub issue for more](https://github.com/FusionAuth/fusionauth-issues/issues/944)).
-If you only provide a `password` field, then FusionAuth will assume the password is in plaintext and hash it for you. Hashing a password negatively affects load time, performance and throughput. If you provide the `salt`, `password`, `encryptionScheme` and `factor` values when importing, then FusionAuth assumes the value in the `password` field is a hashed password, and it will not be hashed.
+If you only provide a `password` field, then FusionAuth will assume the password is in plaintext and hash it for you. Hashing a password negatively affects load time, performance and throughput. If you provide the `salt`, `password`, `encryptionScheme` and `factor` values when importing, then FusionAuth assumes the value in the `password` field is a hashed password, and it will not be hashed.
Deduplicate any emails. In FusionAuth, each email address may be associated with only one user account per tenant.
Stage your data by exporting current user data into JSON files. This will make debugging easier, since you can load one file at a time, and you can repeat a data load if there are issues. It will also be more performant than loading data across a network or from a database.
-#### Building the Migration Scripts
+#### Building The Migration Scripts
To actually move the data, you'll build out a series of scripts and programs. To begin this process, stand up a FusionAuth instance for testing. To start your FusionAuth instance in a known state every time, you may want to configure [Kickstart](/docs/get-started/download-and-install/development/kickstart). A Kickstart file can serve as a foundation for developers and CI processes in the future as well.
@@ -278,13 +279,13 @@ You'll also want to [create an API key](/docs/apis/authentication#managing-api-k
-You can write the migration scripts in shell, any of the supported [client library languages](/docs/sdks/), or against the [REST API](/docs/apis/users) in any language supporting HTTP requests. Iterate over all the users in the old system or systems. Build the JSON files. Add a registration for each application to which a user should have access.
+You can write the migration scripts in shell, any of the supported [client library languages](/docs/sdks/), or against the [REST API](/docs/apis/users) in any language supporting HTTP requests. Iterate over all the users in the old system or systems. Build the JSON files. Add a registration for each application to which a user should have access.
-If you can't build JSON files on the filesystem for some reason, you may build the JSON in memory. This can be a good approach if you are dynamically merging two data sources, but will be tougher to troubleshoot.
+If you can't build JSON files on the filesystem for some reason, you may build the JSON in memory. This can be a good approach if you are dynamically merging two data sources, but will be tougher to troubleshoot.
Finally, import the JSON using the `importUsers` method of a FusionAuth client library or by calling the REST API directly. This is [fully documented](/docs/apis/users#import-users).
@@ -319,11 +320,11 @@ for file in $JSON_FILE_DIR/*.json; do
done
```
-Consult the [documentation for this API](/docs/apis/users#import-users) for more information.
+Consult the [documentation for this API](/docs/apis/users#import-users) for more information.
If the original system hashes passwords using an algorithm other than those [schemes FusionAuth supports](/docs/reference/password-hashes), write and install a [custom password hashing plugin](/docs/extend/code/password-hashes/custom-password-hashing). In either case, specify the scheme in the user import JSON file. (It is called an `encryptionScheme` for backwards compatibility, but is actually a hashing scheme.)
-In FusionAuth, duplicate emails are not allowed within the same tenant. If you may have duplicate emails, de-duplicate them before importing. If you don't want to do so, set the `validateDbConstraints` property to `true` in the import JSON. When this is done, the import API will return a user friendly error message when duplicate addresses are found.
+In FusionAuth, duplicate emails are not allowed within the same tenant. If you may have duplicate emails, de-duplicate them before importing. If you don't want to do so, set the `validateDbConstraints` property to `true` in the import JSON. When this is done, the import API will return a user friendly error message when duplicate addresses are found.
```json title="Import Error Message When validateDbConstraints is true"
{
@@ -357,7 +358,7 @@ The import API expects users to have passwords. If you are migrating some users
You can assign them a high entropy password such as a UUID for the import and then use the [Forgot Password API](/docs/apis/users#start-forgot-password-workflow). This will send them an email to reset their password.
-Another option is to not use the Import API. Instead, import these users one by one using the [User API](/docs/apis/users), which can optionally send a setup password email.
+Another option is to not use the Import API. Instead, import these users one by one using the [User API](/docs/apis/users), which can optionally send a setup password email.
Note that both methods rely on sending an email to these users. It's worth reviewing how many users will fall into this bucket and to ensure that your email sending infrastructure is capable of handling the requests.
@@ -379,13 +380,13 @@ However, you cannot drop the `Default` tenant containing the FusionAuth applicat
When you have an import working well, test your assumptions by pointing applications to FusionAuth for authentication. You've probably made some application changes for the proof of concept, but now test with the real user data that you've just migrated.
-#### Performing the Migration
+#### Performing The Migration
When your scripts work in your testbed environment, prepare to do a production migration. Inform all the internal stakeholders. Plan for downtime unless you can run your application with the original user store in a read only mode. How much downtime? You should know based on your testing of the import.
-Run the migration on your production dataset, moving the data from the original system to FusionAuth. When the migration is finished, release your application changes. All applications should point to FusionAuth for authentication requests and related user flows, including, but not limited to:
+Run the migration on your production dataset, moving the data from the original system to FusionAuth. When the migration is finished, release your application changes. All applications should point to FusionAuth for authentication requests and related user flows, including, but not limited to:
-* Log in
+* Log in
* Registration, if applicable
* Forgot password
* Password changes
@@ -396,7 +397,7 @@ Should you need to rollback, revert the changes to your application pointing it
A segment by segment migration is similar to the above big ban migration, except that you are going to split your user data into segments and migrate each segment. Logical user database segmentation points include by application or by role. However, the planning, mapping and execution are similar, just done for smaller chunks of users and multiple times.
-However, the application cutover process with this approach is not as simple. You can't simply send all your users to FusionAuth when you haven't migrated all of them.
+However, the application cutover process with this approach is not as simple. You can't simply send all your users to FusionAuth when you haven't migrated all of them.
Which users are sent to FusionAuth depends on how you created your segments. If you split on application usage, then update one application to send any authenticating users to FusionAuth. If you split your users based on other attributes, build logic in your application to determine where to send a user when they log in.
@@ -412,14 +413,14 @@ Unlike a big bang migration, with a slow migration you need to set a migration c
So, with this approach, you need to decide what "done" means. Some factors to consider:
-* How often do people log in?
+* How often do people log in?
* Is there a significant long tail of users who visit the application less frequently than the average user?
-* Are there external events such as times of the year or holidays when greater or lesser numbers of users engage with your application?
-* What are the ramifications of a user being unable to log in? Are there business, compliance, legal, or security concerns?
+* Are there external events such as times of the year or holidays when greater or lesser numbers of users engage with your application?
+* What are the ramifications of a user being unable to log in? Are there business, compliance, legal, or security concerns?
* Is loss of timely access to your application an annoyance or a disaster?
* You will have some users who have not migrated when the slow migration period is over. How will you handle those accounts?
* How painful is it to operate both FusionAuth and your current authentication system?
-* How valuable is a customer who has not logged in to your application in six months? A year? Three years?
+* How valuable is a customer who has not logged in to your application in six months? A year? Three years?
Based on these answers, set a goal for a number or proportion of migrated users, the duration of the migration period, or both. If you don't have this, you don't know when to stop the migration. Set a cadence for how often you'll check the number of migrated users and compare it with your goal.
@@ -427,7 +428,7 @@ Make sure you can regularly query FusionAuth to know the number of migrated acco
#### Migration Timeline
-Communicate a timeline for migration to interested parties.
+Communicate a timeline for migration to interested parties.
You can calculate a timeline by knowing the following:
@@ -435,7 +436,7 @@ You can calculate a timeline by knowing the following:
#### Connect To The Original System
-With a slow migration, FusionAuth is connecting to your previous datastore every time a user not currently in FusionAuth authenticates. This connection requires either an HTTP API request or an LDAP call.
+With a slow migration, FusionAuth is connecting to your previous datastore every time a user not currently in FusionAuth authenticates. This connection requires either an HTTP API request or an LDAP call.
If your current user datastore is an LDAP directory, such as ActiveDirectory or OpenLDAP, then you don't need to do anything special to enable the connection; FusionAuth knows how to communicate with LDAP servers.
@@ -451,7 +452,7 @@ This is an example of all the JSON data you could return, but you can omit most
The password is not included. That password will have been hashed according to your FusionAuth tenant password settings.
-The generated JWT will be delivered to the client to present to any resource servers (other API servers, etc). For maximum compatibility, it should have [FusionAuth claim values](/docs/apis/jwt).
+The generated JWT will be delivered to the client to present to any resource servers (other API servers, etc). For maximum compatibility, it should have [FusionAuth claim values](/docs/apis/jwt).
The JSON response above has a `user.data.migrated` value of `true`. This indicates that this user has been migrated. As mentioned above, adding this custom attribute allows you to query migration progress. You can read more about the [Generic Connector requirements](/docs/lifecycle/migrate-users/connectors/generic-connector) in the documentation.
@@ -467,7 +468,7 @@ Configuration varies depending on whether the original datasource is an HTTP API
Configure the connection information, including the URL of the server, the method used to connect to it (LDAPS, STARTTLS), and a system account which can query across all accounts for the directory or section of the directory tree being migrated. You'll also need to specify the user attributes to be queried and returned.
-Map each attribute from the LDAP directory into the FusionAuth user object. You do this with an [LDAP Connector Reconcile Lambda](/docs/extend/code/lambdas/ldap-connector-reconcile).
+Map each attribute from the LDAP directory into the FusionAuth user object. You do this with an [LDAP Connector Reconcile Lambda](/docs/extend/code/lambdas/ldap-connector-reconcile).
@@ -475,7 +476,7 @@ Make sure you uncomment the lines where `user.data.migrated` is set to `true`, a
##### Generic
-With a Generic Connector, configure the URL endpoint and the security settings. Unlike with the LDAP connector, there is no lambda. The mapping of the original system's user data into the FusionAuth data model is performed instead in the HTTP API logic.
+With a Generic Connector, configure the URL endpoint and the security settings. Unlike with the LDAP connector, there is no lambda. The mapping of the original system's user data into the FusionAuth data model is performed instead in the HTTP API logic.
Make sure you use TLS and other security measures to connect to this endpoint, since you'll be sending sensitive user information to it. Full configuration details are available in the [Generic Connector documentation](/docs/lifecycle/migrate-users/connectors/generic-connector).
@@ -499,13 +500,13 @@ With FusionAuth, proxying authentication requests to the original datasource is
Make sure to check the Migrate user checkbox. Then, each user will be authenticated against the original user datastore the first time they are seen. Their data will then be migrated to FusionAuth. On subsequent logins, they'll authenticate with FusionAuth. You can learn more about configuring Connector policies in the [Connector documentation](/docs/lifecycle/migrate-users/connectors/).
-#### Modify Your Application
+#### Modify Your Application
Unlike with a big bang approach, there is no protracted downtime. You simply need to release the changes required for users to authenticate against FusionAuth after configuring and testing the connectors. Make sure you record the number of accounts in the old system just before cutover.
Should you need to rollback, revert all changes made to your application which direct users to FusionAuth. If users updated profile data in FusionAuth, you'll need to port those changes back to your original system. A script using the user API will be a good starting point.
-After you release this modified version of your application, your users will begin their transparent migration into FusionAuth.
+After you release this modified version of your application, your users will begin their transparent migration into FusionAuth.
#### Monitor Progress
@@ -533,7 +534,7 @@ curl 'https://elasticsearch.piedpiper.com/fusionauth_user/_count?q=data.migrated
{"count":62900,"_shards":{"total":5,"successful":5,"skipped":0,"failed":0}}
```
-However you retrieve the number of users migrated, regularly compare it to the number of accounts in the original system before the migration began. This ration will determine if it is time to end the slow migration.
+However you retrieve the number of users migrated, regularly compare it to the number of accounts in the original system before the migration began. This ration will determine if it is time to end the slow migration.
#### Remove The Proxy
@@ -550,8 +551,8 @@ At this point, decide how to handle users who haven't been migrated. You may be
You considered this situation while planning your migration, but now, implement the decision. You could:
* Notify them and encourage them to log in. You can contact them with a message such as: "If you don’t log in by DATE, your account will be deleted".
-* Archive or delete the accounts and their corresponding data. When one of these users comes to your site and tries to sign in, they won't have an account and will be forced to re-register, having lost their data.
-* Move them to FusionAuth via a big bang migration of all unmigrated users.
+* Archive or delete the accounts and their corresponding data. When one of these users comes to your site and tries to sign in, they won't have an account and will be forced to re-register, having lost their data.
+* Move them to FusionAuth via a big bang migration of all unmigrated users.
* Extend the time running both systems; that is, set a new goal and continue the slow migration.
You can mix and match these approaches. For example, you could migrate all paying customers, even those who haven't signed in during the migration period. At the same time you could archive the data of free accounts; those potential customers may have been trialing your application and may even have forgotten they have an account.
@@ -563,11 +564,11 @@ What happens when a user forgets their password and attempts to reset it, but yo
* The user has been migrated and has an account in FusionAuth
* The user has not been migrated and still only has an account in the legacy system
-In either case, when a user submits a forgot password request, FusionAuth will display a message similar to `We've sent you an email containing a link that will allow you to reset your password. Once you receive the email, follow the instructions to change your password.`
+In either case, when a user submits a forgot password request, FusionAuth will display a message similar to `We've sent you an email containing a link that will allow you to reset your password. Once you receive the email, follow the instructions to change your password.`
An email will only be sent in the first case, where the user exists in FusionAuth. In the second case, the user will never receive an email.
-Why, then, is this message displayed for both sets of users?
+Why, then, is this message displayed for both sets of users?
The answer is that FusionAuth *can't distinguish* between users who have not been migrated and users that don't exist. In order to secure your user data and prevent enumeration attacks, FusionAuth doesn't reveal whether there is a valid account for a given username or email.
@@ -579,7 +580,7 @@ The current recommendation is to update the error message to something like:
`We've sent you an email containing a link that will allow you to reset your password. Once you receive the email follow the instructions to change your password. If you don't receive an email, please check your spam folder or contact customer service.`
-Make sure you provide a link to a form, phone number or other means of contacting customer service. Customer service representatives can then check to see whether the user is migrated or not.
+Make sure you provide a link to a form, phone number or other means of contacting customer service. Customer service representatives can then check to see whether the user is migrated or not.
A customer service representative can trigger a password reset email from the appropriate user data store. You can do that from within the FusionAuth administrative user interface.
@@ -675,7 +676,7 @@ If you have SAML identity providers (IdPs) to which your existing user account s
When migrating, there is an existing SAML Service Provider (SP). This could be a custom application or another vendor. After migration, you will use FusionAuth as your new SP.
-There are two options to update this type of SAML configuration external to FusionAuth.
+There are two options to update this type of SAML configuration external to FusionAuth.
#### Updating Upstream Configuration
@@ -712,7 +713,7 @@ When you are changing the Destination assertion policy
-At a later time, request each SAML IdP update their configuration to point to the FusionAuth ACS URL. After all upstream providers have been migrated to the latest configuration, set the Destination assertion policy field to Enabled, the most secure setting.
+At a later time, request each SAML IdP update their configuration to point to the FusionAuth ACS URL. After all upstream providers have been migrated to the latest configuration, set the Destination assertion policy field to Enabled, the most secure setting.
## Additional Resources
diff --git a/astro/src/content/docs/lifecycle/migrate-users/genericmigration.mdx b/astro/src/content/docs/lifecycle/migrate-users/genericmigration.mdx
index b6205fc537..e916f67b24 100644
--- a/astro/src/content/docs/lifecycle/migrate-users/genericmigration.mdx
+++ b/astro/src/content/docs/lifecycle/migrate-users/genericmigration.mdx
@@ -3,34 +3,28 @@ title: Migrate From A Generic Authentication System
description: How to migrate users from your custom authentication system to FusionAuth.
section: lifecycle
subcategory: migrate users
-tertcategory: provider specific
prerequisites: Docker
technology: a custom authentication provider
-importCodeRoot: https://raw.githubusercontent.com/ritza-co/fusionauth-import-scripts/genericmigration
---
import InlineUIElement from 'src/components/InlineUIElement.astro';
import InlineField from 'src/components/InlineField.astro';
import Aside from '/src/components/Aside.astro';
-import {RemoteCode} from '@fusionauth/astro-components';
-import Identifiers from 'src/content/docs/lifecycle/migrate-users/provider-specific/_identifiers.mdx';
import MappingUserAttributes from 'src/content/docs/lifecycle/migrate-users/provider-specific/_mapping-user-attributes.mdx';
-import SocialLoginNote from 'src/content/docs/lifecycle/migrate-users/provider-specific/_social-login-note.mdx';
import SocialLoginMigration from 'src/content/docs/lifecycle/migrate-users/provider-specific/_social-login-migration.mdx';
import OtherEntitiesIntro from 'src/content/docs/lifecycle/migrate-users/provider-specific/_other-entities-intro.mdx';
import ScrollRef from 'src/components/ScrollRef.astro';
import WhatNext from 'src/content/docs/lifecycle/migrate-users/provider-specific/_what-next.mdx';
import AdditionalSupport from 'src/content/docs/lifecycle/migrate-users/provider-specific/_additional-support.mdx';
-export const migration_source_name = 'TODO';
-export const migration_source_dir = 'TODO';
+export const migration_source_dir = 'generic';
export const script_supports_social_logins = 'false';
## Overview
This document will help you migrate users from {frontmatter.technology} to FusionAuth.
-This guide is a low-level, technical tutorial focusing on transferring password hashes, calling APIs, and preparing data when migrating users from {frontmatter.technology}. To understand how to plan a migration at a higher level, please read the [FusionAuth migration guide](/docs/lifecycle/migrate-users/general-migration).
+This guide is a low-level, technical tutorial focusing on transferring password hashes, calling APIs, and preparing data when migrating users from {frontmatter.technology}. For more information on how to plan a migration at a higher level, please read the [FusionAuth migration guide](/docs/lifecycle/migrate-users/general-migration).
## Prerequisites
@@ -38,35 +32,46 @@ If you want to import user passwords in addition to user personal details, you n
To follow this tutorial, you need [Docker](https://docs.docker.com/get-docker/) to run an example web application and the migration scripts.
-If you prefer to run scripts locally, you will need Node.js installed on your physical machine. You will also need to change occurrences of `db` and `host.docker.internal` to `localhost` in all the scripts.
+
+
+If you prefer to run scripts directly on your machine, you will need Node.js installed locally. You will also need to change occurrences of `db` and `host.docker.internal` to `localhost` in all the scripts.
-### Obtaining User Data
+## Planning Considerations
### Mapping User Attributes
-
+
### Social Logins
-
-
### Other Entities
-
+
+
+* If your application makes use of roles, FusionAuth has [roles](/docs/get-started/core-concepts/roles) that are configured on an application-by-application basis and made available in a token after successful authentication.
+* In FusionAuth, you can manage a set of users via a [Tenant](/docs/get-started/core-concepts/tenants).
+* If your application sends emails like forgotten password notifications, FusionAuth has this functionality, and [the templates are customizable](/docs/customize/email-and-messages/).
+* In FusionAuth, custom user attributes are stored on the `user.data` field and are dynamic, searchable, and unlimited in size. Any valid JSON value may be stored in this field.
+* If your application uses multi-factor authentication (MFA), FusionAuth [supports MFA](/docs/lifecycle/authenticate-users/multi-factor-authentication), and you can enable it for a tenant and configure it for a user at any time.
#### Identifiers
-When creating an object with the FusionAuth API, you can specify the Id. It must be a [UUID](/docs/reference/data-types#uuids).
+When you create an object with the FusionAuth API, you can specify the Id. It must be a [UUID](/docs/reference/data-types#uuids). This works for users, applications, tenants, and others.
+
+## Exporting Users
-This works for users, applications, and tenants, among others.
+Let's consider a minimal web application to demonstrate how to migrate users and authentication to FusionAuth. This example app only has a sign-in page, a restricted account details page, and a PostgreSQL database with a single table to hold user passwords.
-## Create An Example Application With Custom Authentication
-Let's consider a minimal web application to demonstrate how to migrate users and authentication to FusionAuth. This app has only a sign-in page and a restricted page, and a PostgreSQL database with a single table to hold user passwords.
+### Create An Example Application With Custom Authentication
To get the code used in this tutorial, clone the Git repository below.
@@ -74,7 +79,7 @@ To get the code used in this tutorial, clone the Git repository below.
git clone https://github.com/FusionAuth/fusionauth-import-scripts
```
-The `generic` directory contains all the code you need for this tutorial, and `generic/exampleData` has the output of the scripts.
+The `generic` directory contains all the code you need for this tutorial, and `generic/exampleData` contains the output of the scripts.
Navigate to the `generic/src` directory.
@@ -82,22 +87,21 @@ Navigate to the `generic/src` directory.
cd fusionauth-import-scripts/generic/src
```
-Start a Docker container for the database and app by running the command below in a terminal.
+Start a Docker container for the app and database by running the command below in a terminal.
```sh
-docker compose up --file 1_appDockerCompose.yaml
+docker compose --file 1_appDockerCompose.yaml up
```
### Create The User Table
-Now that the database is running you need to create a table to hold users. In a new terminal, run the commands below.
+Now that the database is running, you need to create a table to hold users. Open a new terminal in the `fusionauth-import-scripts/generic/src` directory and run the command below.
```sh
-cd fusionauth-import-scripts/generic/src
-docker exec --interactive --tty app sh
+docker exec --interactive --tty app sh
```
-This will connect to the JS container running in Docker. Here is where you will run all the JavaScript scripts. Run the code below in this terminal to create the user table.
+This will connect to the app container running in Docker and start an interactive terminal. You will run all the JavaScript scripts in this interactive Docker terminal. Run the code below in this terminal to create the user table.
```sh
cd /workspace
@@ -109,11 +113,11 @@ The `2_createUser.mjs` script creates a table with the text fields `email`, `has
If you want to see the table, browse the database in any database IDE that can connect to PostgreSQL. [DBeaver](https://dbeaver.io/download) is a free, cross-platform IDE you can use.
-Create a new connection to `localhost`, port `7770`, database `p`, username `p`, password `p`. Open the connection and expand the database tables to the `user` table.
+Create a new connection to `localhost`, port `7770`, database `p`, username `p`, and password `p`. Open the connection and expand the database tables to see the `user` table.
### Run The Web App
-Run the code below in the Docker terminal to start the minimal Express.js web app.
+Run the command below in the interactive Docker terminal to start the minimal Express web app.
```sh
node 3_webApp.mjs
@@ -121,23 +125,23 @@ node 3_webApp.mjs
Browse to `http://localhost:7771/account`. This is a restricted page. Since you are not authenticated, you are not able to view it.
-Browse to `http://localhost:7771`. On the authentication page displayed, enter a random email like `t@t.com` and password `q`.
+Browse to `http://localhost:7771`. On the authentication page displayed, enter a random email like `user@example.com` and password `password`.
-The user will be created in the database, and you will be redirected to the account page. Now you will be able to see the page as you have a cookie in your browser with your email address.
+The user will be created in the database, and you will be redirected to the account page. Now you will be able to see the page as you have a cookie in your browser with the user's email address.
-Open `3_webApp.mjs` and take a look. It has two GET routes to display the home page and account page. The POST route for the home page is more complex. It does the following:
-- Check if a username and password have been entered.
-- Query the database to see if the email exists.
- - If so, compare the password hash in the database with the hash of the password entered.
- - If not, create the user and save their password hashed with a random UUID salt.
+Open `3_webApp.mjs` and take a look. It has two GET routes to display the home page and account page. The POST route for the home page is more complex. It does the following:
+- Checks if username and password have been entered.
+- Queries the database to see if the email exists.
+ - If the email exists, compares the password hash in the database with the hash of the password entered.
+ - If not, creates the user and saves their password hashed with a random UUID salt.
-The `getHash` function at the bottom of the file creates a password hash using SHA256. This is a simple algorithm [supported natively by FusionAuth](https://fusionauth.io/docs/reference/password-hashes#salted-sha-256). If your real application uses an unusual hashing algorithm, you can write a custom hashing plugin for FusionAuth. Other migration guides in this documentation category have examples of how to do this.
+The `getHash` function at the bottom of the file creates a password hash using SHA256. This is a simple algorithm, [supported natively by FusionAuth](/docs/reference/password-hashes#salted-sha-256). If your real application uses an uncommon hashing algorithm, you can [write a custom hashing plugin for FusionAuth](/docs/extend/code/password-hashes/writing-a-plugin).
-In the terminal, push Ctrl-C to end the application server.
+In the interactive Docker terminal, click Ctrl + C to stop the application server.
-## Export Users
+### Create A Users File
-Run the command below to export your users to the file `users.json`.
+Run the command below in the interactive Docker terminal to export your users to the file `users.json`.
```sh
node 4_exportUsers.mjs
@@ -145,7 +149,7 @@ node 4_exportUsers.mjs
In reality, you could create a JSON file of users from your application in whatever language suits you — most likely a SQL script run directly against your database.
-The next script, `5_convertUserToFaUser.mjs`, is the most important. It maps the fields of `users.json` to FusionAuth fields. The tiny example app has only email and password, so you will want to alter this script significantly for your real app. All available FusionAuth fields are commented out for you to use.
+The next script, `5_convertUserToFaUser.mjs`, is the most important. It maps the fields of `users.json` to FusionAuth fields. The tiny example app has only email and password, so you will want to alter this script significantly for your real app. The attributes of the User object in FusionAuth are [well documented here](/docs/apis/users).
The script uses `stream-json`, a JSON library that can incrementally read massive files with millions of users. It opens the `users.json` file for reading in the line `new Chain([fs.createReadStream(inputFilename), parser(), new StreamArray(),]);`. For more information, read https://github.com/uhop/stream-json. The `processUsers()` function calls `getFaUserFromUser()` to map your user to FusionAuth, and then saves them to an `faUsers.json` file.
@@ -155,60 +159,68 @@ The `getFaUserFromUser()` function does a few things:
- Uses the hashing algorithm name in `faUser.encryptionScheme = 'salted-sha256';`. The salt is converted to Base64 to meet FusionAuth requirements.
- Adds Registrations (a Role link between a User and an Application) for users. You will need to change these Ids to match those of your application when doing a real migration.
-If you are uncertain about what a user attribute in FusionAuth does, read more in the [user guide](/docs/apis/users), as linked in the [general migration guide](/docs/lifecycle/migrate-users/general-migration) recommended earlier.
+If you are uncertain about what a user attribute in FusionAuth does, read more in the [user guide](/docs/apis/users), as linked in the [general migration guide](/docs/lifecycle/migrate-users/general-migration).
-In the terminal run the script with the following command.
+In the interactive Docker terminal, run the script with the following command.
```sh
node 5_convertUserToFaUser.mjs
```
-Your output should be valid JSON and look like the file `fusionauth-import-scripts/exampleData/faUsers.json`.
+Your output should be valid JSON and look like the file `fusionauth-import-scripts/generic/exampleData/faUsers.json`.
-## Import Users
+## Importing Users
If you are not already running FusionAuth or want to test this process on another instance, you can start FusionAuth in Docker.
-Open a new terminal in the `fusionauth-import-scripts` directory and run the code below (this is your 3rd and last terminal for this guide)
+Open a new terminal in the `fusionauth-import-scripts` directory and run the commands below.
```sh
-cd fusionauth-import-scripts/generic/fusionAuthDockerFiles
+cd generic/fusionAuthDockerFiles
docker compose up
```
FusionAuth will now be running and browsable at `http://localhost:9011`. You can log in to the [FusionAuth admin UI](http://localhost:9011/admin) with `admin@example.com` and `password`. The container is called `fa`.
-Now you have the users file `faUsers.json` and the FusionAuth is running. To import the users into FusionAuth, you need to run the Node.js import script.
+This configuration makes use of a bootstrapping feature of FusionAuth called [Kickstart](/docs/get-started/download-and-install/development/kickstart), defined in `fusionauth-import-scripts/generic/fusionAuthDockerFiles/kickstart/kickstart.json`. When FusionAuth comes up for the first time, it will look at the `kickstart.json` file and configure FusionAuth to the specified state. In summary, the defined Kickstart sets up an API Key, an admin user to log in with, a theme, and a Test application in FusionAuth.
+
+Now you have the users file `faUsers.json`, and FusionAuth is running. To import the users into FusionAuth, you need to run the Node.js import script.
-In the Docker terminal, run the command below.
+In the interactive Docker terminal, run the command below.
```sh
node 6_importUsers.mjs
```
-This script uses the FusionAuth SDK for Node.js `@fusionauth/typescript-client`. It's used only for a single operation, `fa.importUsers(importRequest)`. For more information, read the [FusionAuth Typescript Client Library](/docs/sdks/typescript) documentation.
+This script uses the FusionAuth SDK for Node.js `@fusionauth/typescript-client`. It's used only for a single operation, `fa.importUsers(importRequest)`. For more information, read the [FusionAuth TypeScript Client Library](/docs/sdks/typescript) documentation.
This script imports users individually. If this is too slow when running the production migration, wrap the `importUsers()` FusionAuth SDK call in a loop that bundles users in batches of 1000.
### Verify The Import
-If the migration script ran successfully, you should be able to log in to the `Test` application with one of the imported users. In the [FusionAuth admin UI](http://localhost:9011/admin), navigate to **Applications —> WordpressTestImportApp**. Click the View button (green magnifying glass) next to the application and note the OAuth IdP login URL.
+If the migration script ran successfully, you should be able to log in to the `Test` application with one of the imported users. In the [FusionAuth admin UI](http://localhost:9011/admin), navigate to **Applications —> Test**. Click the View button (green magnifying glass) next to the application and note the OAuth IdP login URL.
-Copy this URL and open it in a new incognito browser window. (If you don’t use an incognito window, the admin user session will interfere with the test.) You should see the login screen. Enter username `t@t.com` and password `q`. Login should work.
+Copy this URL and open it in a new incognito browser window. (If you don’t use an incognito window, the admin user session will interfere with the test.) You should see the login screen. Enter username `user@example.com` and password `password`. Login should work.
+
+
Next, log in to the [FusionAuth admin UI](http://localhost:9011/admin) with `admin@example.com` and password `password`. Review the user entries to ensure the data was correctly imported.
-You can manage a user by clicking on the Manage button (black button) to the right of the user in the list of users to review the details of the imported user’s profile. In the Source tab, you can see all the user details as a JSON object.
+Click the Manage button (black button) to the right of a user in the list of users to review the details of the imported user’s profile. In the Source tab, you can see all the user details as a JSON object.
#### Debug With The FusionAuth Database
If you have errors logging in, you can use the FusionAuth database directly to see if your users were imported, and check their hashes manually.
-You can use any PostgreSQL browser. DBeaver will work. The connection details are in `fusionauth-import-scripts/generic/fusionAuthDockerFiles/docker-compose.yml` and `.env`.
+You can use any PostgreSQL browser. DBeaver will work. The connection details are in the files `docker-compose.yml` and `.env` in the `fusionauth-import-scripts/generic/fusionAuthDockerFiles/` directory.
In your database IDE, create a new PostgreSQL connection with the following details:
@@ -219,29 +231,33 @@ In your database IDE, create a new PostgreSQL connection with the following deta
- Username: `fusionauth`
- Password: `hkaLBM3RVnyYeYeqE3WI1w2e4Avpy0Wd5O3s3`
-Log in to the database and browse to `Databases/fusionauth/Schemas/public/Tables`. Tables `identities` and `users` will show you the login credentials and user personal information.
+Log in to the database and browse to `Databases/fusionauth/Schemas/public/Tables`. The `identities` and `users` tables will show the login credentials and user personal information.
## Use FusionAuth As Your Authentication Provider
Now that your users have been migrated into FusionAuth, how do you authenticate them in your app?
-The first step is to set your OAuth callback URL in the FusionAuth web interface. Edit your `Test` application and set the callback URL to `http://localhost:7771/callback`.
+The first step is to set your OAuth callback URL in the [FusionAuth admin UI](http://localhost:9011/admin). Under **Applications** edit your `Test` application and set the Authorized redirect URLs to `http://localhost:7771/callback`.
+
+
-Now run the command below in the Docker terminal to see the original Express app rewritten to use FusionAuth.
+Now run the command below in the interactive Docker terminal to see the original Express app rewritten to use FusionAuth for authentication.
```sh
node 7_webAppWithFa.mjs
```
-Browse to `http://localhost:7771`. You'll see that the sign-in page has been replaced by FusionAuth. You can style this page however you like. For more information see the full [quickstart guide for Express](/docs/quickstarts/quickstart-javascript-express-web).
+Browse to `http://localhost:7771`. You'll see that the sign-in page has been replaced by FusionAuth. You can style this page however you like. For more information, see the full [quickstart guide for Express](/docs/quickstarts/quickstart-javascript-express-web).
-The new application code looks very similar to the original, except that the login and hashing code has been replaced by OAuth2 calls to FusionAuth via the Node.js [Passport library](https://www.npmjs.com/package/passport).
+The new application code looks very similar to the original, except that the login and hashing code has been replaced by OAuth 2.0 calls to FusionAuth via the Node.js [Passport library](https://www.npmjs.com/package/passport).
-Note that the bottom of the file stores secrets directly in the code. In reality, you should move these to a `.env` file that is not checked in to GitHub. Be aware once again of using `localhost` instead of `host.docker.internal` for URLs that call a server directly (browser URLs still use `localhost`).
+
### Delete The Docker Containers
-Push Ctrl-C in all terminals to end the Docker instances. Run the code below on your host machine to remove the Docker containers and images.
+Push Ctrl + C in all terminals to stop the Docker instances. Run the code below on your host machine to remove the Docker containers and images if you are done testing.
```sh
docker rm app app_db fa fa_db
diff --git a/astro/src/content/docs/lifecycle/migrate-users/provider-specific/_final-destination.mdx b/astro/src/content/docs/lifecycle/migrate-users/provider-specific/_final-destination.mdx
index af702a86ab..f8dbce8ae3 100644
--- a/astro/src/content/docs/lifecycle/migrate-users/provider-specific/_final-destination.mdx
+++ b/astro/src/content/docs/lifecycle/migrate-users/provider-specific/_final-destination.mdx
@@ -8,5 +8,4 @@ If you need to start over because the import failed or you need to tweak a setti
-Confirm your desire to delete the tenant. Depending on how many users you have imported, this may take some time.
-
+Confirm your desire to delete the tenant. Depending on how many users you have imported, this may take some time.
\ No newline at end of file
diff --git a/astro/src/content/docs/lifecycle/migrate-users/provider-specific/auth0.mdx b/astro/src/content/docs/lifecycle/migrate-users/provider-specific/auth0.mdx
index 52fb8dc380..7782067bef 100644
--- a/astro/src/content/docs/lifecycle/migrate-users/provider-specific/auth0.mdx
+++ b/astro/src/content/docs/lifecycle/migrate-users/provider-specific/auth0.mdx
@@ -62,7 +62,7 @@ Here's a brief video walking through the export process, which is further docume
### Other Entities
-
+
* In Auth0, [Connections](https://auth0.com/docs/identityproviders) are a source of data for users. FusionAuth calls these [Identity Providers](/docs/lifecycle/authenticate-users/identity-providers/).
* [Rules](https://auth0.com/docs/rules), [Hooks](https://auth0.com/docs/hooks) and [Actions](https://auth0.com/docs/actions) are ways for you to customize authentication or authorization workflows. FusionAuth has a similar concept called [Lambdas](/docs/extend/code/lambdas/).
diff --git a/astro/src/content/docs/lifecycle/migrate-users/provider-specific/azureadb2c.mdx b/astro/src/content/docs/lifecycle/migrate-users/provider-specific/azureadb2c.mdx
index 9025fdd568..b27dabe04b 100644
--- a/astro/src/content/docs/lifecycle/migrate-users/provider-specific/azureadb2c.mdx
+++ b/astro/src/content/docs/lifecycle/migrate-users/provider-specific/azureadb2c.mdx
@@ -87,7 +87,7 @@ To retrieve the user information, use the approach documented in the
+
* [User Flows and Custom Policies](https://docs.microsoft.com/en-us/azure/active-directory-b2c/user-flow-overview) are ways for you to customize authentication or authorization workflows with Azure AD B2C. FusionAuth has a similar concept called [Lambdas](/docs/extend/code/lambdas/). FusionAuth also has [webhooks](/docs/extend/events-and-webhooks/) fired at certain points in the user lifecycle; in certain configurations, they can also stop a particular authentication flow.
* Azure AD B2C does not have a standardized roles-and-permissions concept. Roles and permissions are usually handled with custom attributes or secondary data stores. FusionAuth has [roles](/docs/get-started/core-concepts/roles) that are configured on an application by application basis and made available in a token after a successful authentication.
@@ -720,7 +720,7 @@ You are now ready to transform your data, and import it to FusionAuth. We've cre
### Get the Script
-
+
### Install Needed Gems
diff --git a/astro/src/content/docs/lifecycle/migrate-users/provider-specific/duende.mdx b/astro/src/content/docs/lifecycle/migrate-users/provider-specific/duende.mdx
index 7078a8b599..ed41653ed1 100644
--- a/astro/src/content/docs/lifecycle/migrate-users/provider-specific/duende.mdx
+++ b/astro/src/content/docs/lifecycle/migrate-users/provider-specific/duende.mdx
@@ -54,7 +54,7 @@ Duende IdentityServer does not prescribe where you store your user data. You can
### Other Entities
-
+
* In Duende IdentityServer, External Identity Providers are a source of data for users. FusionAuth calls these Identity Providers.
* Login flows with Duende IdentityServer are highly customizable using custom code in your host application. FusionAuth authentication flows can be customized with a feature called [Lambdas](/docs/extend/code/lambdas/).
@@ -65,7 +65,7 @@ Duende IdentityServer does not prescribe where you store your user data. You can
* Since Duende IdentityStore is a framework used to build custom authentication servers, the actual functionality and features of the particular custom server you are migrating from could vary widely. FusionAuth may not have equivalents for these custom features and functionality, and is focused primarily on authentication.
@@ -96,9 +96,9 @@ We recommend exporting your user information in JSON format so that you can impo
We've created a sample C# export program to export users from a Duende IdentityServer database to a JSON file. This program assumes you are using [ASP.NET Identity](https://learn.microsoft.com/en-us/aspnet/core/security/authentication/identity?view=aspnetcore-7.0&tabs=visual-studio). You can find the folder with the source code for the export program [here](https://github.com/fusionauth/fusionauth-import-scripts/tree/identityserver/duende/identity-server-export).
-Clone the repo and open the solution in Visual Studio Code.
+Clone the repo and open the solution in Visual Studio Code.
-Update the connectionString variable in the `Program.cs` file with your database connection information.
+Update the connectionString variable in the `Program.cs` file with your database connection information.
The export program assumes that user data is stored in the standard ASP.NET Identity tables, namely `AspNetUsers` and `AspNetUserLogins`. If you are using a different table or have extended the ASP.NET Identity tables, you will need to update the program to select from the correct tables. You can do this by modifying the `SqlCommand` in the `Program.cs` file. You can also modify the mapping of the user information to the `User` export objects in the `Program.cs` file.
@@ -203,7 +203,7 @@ FusionAuth has a repository for [community-contributed examples and code](https:
* Install [Savant](http://savantbuild.org/).
* Run `mvn install` to test that the plugin builds.
* Run `mvn clean compile package` to create the plugin JAR file.
-* Copy the plugin `.jar` file from the `./target` directory to the `plugin` directory of your FusionAuth installation.
+* Copy the plugin `.jar` file from the `./target` directory to the `plugins` directory of your FusionAuth installation.
** On Linux and macOS, the plugin directory is `/usr/local/fusionauth/plugins`.
** On Windows, the plugin directory is `\fusionauth\plugins`.
* Restart FusionAuth.
@@ -214,7 +214,7 @@ If the plugin doesn't show up, please review the [plugin troubleshooting steps](
### Get the Script
-
+
### Install Needed Gems
diff --git a/astro/src/content/docs/lifecycle/migrate-users/provider-specific/firebase.mdx b/astro/src/content/docs/lifecycle/migrate-users/provider-specific/firebase.mdx
index a8f6b1aa45..0dfafac518 100644
--- a/astro/src/content/docs/lifecycle/migrate-users/provider-specific/firebase.mdx
+++ b/astro/src/content/docs/lifecycle/migrate-users/provider-specific/firebase.mdx
@@ -66,7 +66,7 @@ to do so.
### Other Entities
-
+
* In Firebase, sign-in providers are a source of data for users.
FusionAuth calls these Identity Providers.
@@ -268,7 +268,7 @@ If that plugin doesn't show up, please review the [plugin troubleshooting steps]
### Get the Script
-
+
### Install Needed Gems
diff --git a/astro/src/content/docs/lifecycle/migrate-users/provider-specific/forgerock.mdx b/astro/src/content/docs/lifecycle/migrate-users/provider-specific/forgerock.mdx
index 6e0d4225ed..4f7e8c92fc 100644
--- a/astro/src/content/docs/lifecycle/migrate-users/provider-specific/forgerock.mdx
+++ b/astro/src/content/docs/lifecycle/migrate-users/provider-specific/forgerock.mdx
@@ -62,7 +62,7 @@ There are many ways to get data from the {migration_source_name} instances. If y
### Other Entities
-
+
* In {migration_source_name}, Identity Providers and User Federation allow user data to remain in external systems of record. FusionAuth has similar concepts of [Identity Providers](/docs/lifecycle/authenticate-users/identity-providers/) and [Connectors](/docs/lifecycle/migrate-users/connectors/).
* Mappers are ways for you to customize authentication or authorization workflows. FusionAuth has a similar concept called [Lambdas](/docs/extend/code/lambdas/).
diff --git a/astro/src/content/docs/lifecycle/migrate-users/provider-specific/keycloak.mdx b/astro/src/content/docs/lifecycle/migrate-users/provider-specific/keycloak.mdx
index c6db1ca698..41b5984915 100644
--- a/astro/src/content/docs/lifecycle/migrate-users/provider-specific/keycloak.mdx
+++ b/astro/src/content/docs/lifecycle/migrate-users/provider-specific/keycloak.mdx
@@ -71,7 +71,7 @@ Make note of each realm name containing users you want to migrate. You may migra
### Other Entities
-
+
* In Keycloak, Identity Providers and User Federation allow user data to remain in external systems of record. FusionAuth has similar concepts of [Identity Providers](/docs/lifecycle/authenticate-users/identity-providers/) and [Connectors](/docs/lifecycle/migrate-users/connectors/).
* Mappers are ways for you to customize authentication or authorization workflows. FusionAuth has a similar concept called [Lambdas](/docs/extend/code/lambdas/).
@@ -175,7 +175,7 @@ If you have configured Keycloak to use a different hashing algorithm, you will n
### Get the Script
-
+
### Install Needed Gems
diff --git a/astro/src/content/docs/lifecycle/migrate-users/provider-specific/pingone.mdx b/astro/src/content/docs/lifecycle/migrate-users/provider-specific/pingone.mdx
index 37af6a9a0e..6d85c1d8ad 100644
--- a/astro/src/content/docs/lifecycle/migrate-users/provider-specific/pingone.mdx
+++ b/astro/src/content/docs/lifecycle/migrate-users/provider-specific/pingone.mdx
@@ -571,7 +571,7 @@ Set up a Python virtual environment as described in the
+
Run the script with the following command.
diff --git a/astro/src/content/docs/lifecycle/migrate-users/provider-specific/stytch.mdx b/astro/src/content/docs/lifecycle/migrate-users/provider-specific/stytch.mdx
new file mode 100644
index 0000000000..2d23e16d5b
--- /dev/null
+++ b/astro/src/content/docs/lifecycle/migrate-users/provider-specific/stytch.mdx
@@ -0,0 +1,383 @@
+---
+title: Migration From Stytch
+description: How to migrate your users from Stytch to FusionAuth.
+section: lifecycle
+subcategory: migrate users
+tertcategory: provider specific
+prerequisites: Docker, Node
+importCodeRoot: https://raw.githubusercontent.com/FusionAuth/fusionauth-import-scripts/master
+javaCodeRoot: https://raw.githubusercontent.com/FusionAuth/fusionauth-contrib/master
+---
+
+import InlineUIElement from 'src/components/InlineUIElement.astro';
+import InlineField from 'src/components/InlineField.astro';
+import Aside from '/src/components/Aside.astro';
+import {RemoteCode} from '@fusionauth/astro-components';
+import AdditionalSupport from 'src/content/docs/lifecycle/migrate-users/provider-specific/_additional-support.mdx';
+import Identifiers from 'src/content/docs/lifecycle/migrate-users/provider-specific/_identifiers.mdx';
+import MappingUserAttributes from 'src/content/docs/lifecycle/migrate-users/provider-specific/_mapping-user-attributes.mdx';
+import SocialLoginNote from 'src/content/docs/lifecycle/migrate-users/provider-specific/_social-login-note.mdx';
+import SocialLoginMigration from 'src/content/docs/lifecycle/migrate-users/provider-specific/_social-login-migration.mdx';
+import OtherEntitiesIntro from 'src/content/docs/lifecycle/migrate-users/provider-specific/_other-entities-intro.mdx';
+import WhatNext from 'src/content/docs/lifecycle/migrate-users/provider-specific/_what-next.mdx';
+import ScrollRef from 'src/components/ScrollRef.astro';
+
+export const migration_source_name = 'Stytch';
+export const migration_source_dir = 'stytch';
+export const script_supports_social_logins = 'true';
+
+## Overview
+
+This document will help you migrate users from Stytch to FusionAuth.
+
+There are a number of different ways applications can be integrated with Stych, and it would be difficult to cover them all. This guide is a low-level, technical tutorial focusing on transferring password hashes, calling APIs, and preparing data when migrating users from a Consumer Authentication project. The steps outlined here have not been tested with the Stytch B2B SaaS Authentication project type.
+
+This guide explains how to import passwords into FusionAuth, but does not deal with other Stytch authentication types like magic links, passkeys, passcodes, mobile biometrics, two-factor authentication, and social logins such as Google OAuth.
+
+For an explanation of the high-level process of a migration strategy, see the [migration overview article](/docs/lifecycle/migrate-users/general-migration). Be aware of all laws regarding the protection and transfer of personal information in your country.
+
+## Prerequisites
+
+If you want to import user passwords in addition to user personal details, you need a basic understanding of how password hashing and salts work. FusionAuth has a [hashing article](/articles/security/math-of-password-hashing-algorithms-entropy) that is a good starting point.
+
+To follow this tutorial, you need:
+- [Node.js](https://nodejs.org/en) to run the migration scripts, and npm.
+- [FusionAuth](/download). The easiest way to run it locally is to use [Docker](https://docs.docker.com/get-docker/) with the configuration file provided later in this tutorial.
+
+Stytch also has SDKs for [Go, Java, Python, and Ruby](https://stytch.com/docs/sdks) if you prefer to convert the JavaScript scripts accompanying this tutorial to another language for your migration in production.
+
+## Planning Considerations
+
+### Obtaining User Data
+
+You can use the [Stytch API](https://stytch.com/docs/api) to export user data, but you cannot export password hashes via the API. To get password hashes, you will need to email Stytch support as described in the section.
+
+### Mapping User Attributes
+
+
+
+### Social Logins
+
+
+
+
+
+### Other Entities
+
+
+
+* In Stytch, sign-in providers are a source of data for users. FusionAuth calls these Identity Providers.
+
+#### Identifiers
+
+
+
+## Exporting Users
+
+To export users from Stytch, you need to request the user password hashes from Stytch via email, get all user details via the API, and then combine the user details with the hashes.
+
+### Create Stytch Users
+
+You probably already have a Stytch account with users you want to export to FusionAuth. Even so, it is a good idea to create a separate test project in Stytch with only a few users. You can use the test project to run an end-to-end migration between Stytch and FusionAuth quickly, without having to handle millions of real users and the privacy risks of handling their data when encountering errors.
+
+Create a new Stytch project:
+
+- Create a Stytch account or sign in to your existing account at https://stytch.com/dashboard.
+- Locate the project button next to the workspace button in the header at the top left and use the project button to create a new project you'll use for test users.
+- Under Configuration on the sidebar, select API Keys.
+- Note the project Id and secret.
+
+Create three example users:
+- Download this tutorial's scripts repository from https://github.com/fusionauth/fusionauth-import-scripts and unzip it, or if you have Git, run the code below.
+
+ ```sh
+ git clone https://github.com/fusionauth/fusionauth-import-scripts.git
+ ```
+- In the file `fusionauth-import-scripts/stytch/js/1_makeStytchUsers.mjs`, change the project Id and secret to match your project.
+- In a terminal, run the code below to create three new users in Stytch using the Stytch JavaScript API.
+ ```sh
+ cd fusionauth-import-scripts/stytch/js
+ npm update
+ node 1_makeStytchUsers.mjs
+ ```
+- In the Stytch web dashboard, check that the users now exist.
+- If any errors occur and you need to delete the users, uncomment the lines with `client.users.delete`, set the user Ids from the dashboard, and rerun the script.
+
+
+
+### Request User Passwords From Stytch
+
+You cannot download your users' password hashes from Stytch using their API. To get the password hashes, email support@stytch.com from the email address you used to sign up with Stytch and ask for your users' hashes to be sent to you.
+
+Stytch will encrypt the hashes with your public key. Attach this key in a `.pem` file to the email you send Stytch.
+
+To create a private and public key pair, use the commands below in a terminal.
+
+```sh
+openssl genpkey -algorithm RSA -out private_key.pem &&
+openssl rsa -pubout -in private_key.pem -out public_key.pem
+```
+
+Email Stytch only `public_key.pem`. Keep `private_key.pem` secret and secure.
+
+An example of what these keys look like is in the directory `fusionauth-import-scripts/stytch/exampleData/1_emailRequest`. You can use the keys to request test users from Stytch but do **not** use these keys for real users, as they are publicly available on GitHub.
+
+
+
+### Decrypt The Reply
+
+Stytch will reply with two files: An encrypted password hash file (`stytch-project-test-36510638-652a-4d3d-9a94-f0a7106582fc-hashes-2021-01-11.enc`) and the key to decrypt it (`key.bin.enc`).
+
+Decrypt the password file with the commands below.
+
+```sh
+openssl pkeyutl -decrypt -inkey private_key.pem -in key.bin.enc -out key.bin &&
+openssl enc -d -aes-256-cbc -in stytch-project-test-36510638-652a-4d3d-9a94-f0a7106582fc-hashes-2021-01-11.enc -out stytch_password_hashes.csv -pass file:./key.bin
+```
+
+Now you have all your user hashes in the file `stytch_password_hashes.csv`. Below is the `exampleData/3_responseDecryption/stytch_password_hashes.csv` file content for three users.
+
+
+
+The file header describes the parameters you need to use when you run the scrypt hashing algorithm to convert user passwords to match the hashes in this file.
+
+Below the header are the column names, and then one row per user. In the example data, all hashes were made using scrypt.
+
+#### Common Problems With Hashes
+
+Not all hash algorithms use separate salts. For instance, bcrypt will create a salt automatically when hashing a password, and store the hash and salt concatenated in one string. You may have to deal with the peculiarities of different algorithms like this if you previously migrated users into Stytch from an authentication provider that did not use scrypt.
+
+Even when using scrypt with the given parameters, you need to carefully check that how you hash passwords has the same result as Stytch. Hashes and salts are arrays of bytes (numbers) and therefore cannot be displayed directly as text. They are instead mapped to text using a conversion process called Base64. However, there are [different ways](https://en.wikipedia.org/wiki/Base64#Variants_summary_table) to do this mapping. Since you can see the hashes from Stytch use `-` and `_`, they must use RFC 4648 (the URL-safe standard). This can cause miscommunications with other hash libraries that use `+` and `/`.
+
+For instance, we can use the snippet of JavaScript Node.js code below in `js/2_checkHash.mjs` to create a hash for the last user in the example file.
+
+
+
+Note that our hash algorithm looks correct, but the hashes differ by one character, `+`, in `8dg6AaIWPfcLTQU7lb4H-CI49dHeqaBXfFE1ogb2qRQ=` and `8dg6AaIWPfcLTQU7lb4H+CI49dHeqaBXfFE1ogb2qRQ=`.
+
+To have the hashes match correctly, you need to replace the `+` and `/` characters in your hash:
+
+```js
+const keyBase64 = derivedKey.toString('base64').replace(/\+/g, '-').replace(/\//g, '_');
+```
+
+JavaScript and FusionAuth use the `+` and `/` symbols. Stytch and the Java scrypt plugin use `-` and `_`. If you encounter an error when verifying hashes, try converting the hash and salt to use the other character set.
+
+### Get All User Details And Combine With Hashes
+
+Stytch emails you only the user hash and Id. You need to use the Stytch API to retrieve all user details from Stytch, then combine those with the hash from the email, and save the user to FusionAuth.
+
+Open your hash file from Stytch in a text editor and remove all the header lines so only one user per row remains. Save this file to `fusionauth-import-scripts/stytch/js/hash.csv`. The file should look like `exampleData/4_hashFilePreparation/hash.csv` now.
+
+
+
+Check the fourth column to ensure that only scrypt users are included. The first four columns are the most important: `id`, `hash`, `salt`, and `hash_method`.
+
+The `3_getUserDetails.mjs` script loops through each user in the `hash.csv` file, uses the API to get the user details from Stytch, and saves them to `js/users.json`.
+
+- Open `3_getUserDetails.mjs` and set your project Id and secret.
+- Make sure the `hash.csv` is the same directory `fusionauth-import-scripts/stytch/js` where the `3_getUserDetails.mjs` script is located.
+- Run the commands below in a terminal in the `fusionauth-import-scripts/stytch/js` directory.
+ ```sh
+ npm update
+ node 3_getUserDetails.mjs
+ ```
+
+Open `js/users.json`. It should look like the sample in `exampleData/5_userDetailAndHashPreparation/users.json`.
+
+
+
+
+
+The last lines of the object show the hash and salt that the script added from the hash file to the user details from the API.
+
+## Importing Users
+
+First install a FusionAth plugin to handle the Stytch password hash algorithm, then import the users, and finally verify the import.
+
+### Build The Scrypt Password Hash Plugin For FusionAuth
+
+The scrypt hashing algorithm is not [natively supported by FusionAuth](/docs/reference/password-hashes). However, FusionAuth allows [custom plugins](/docs/extend/code/password-hashes/custom-password-hashing). There is a scrypt plugin accompanying this article [in this GitHub repository](https://github.com/FusionAuth/fusionauth-contrib/tree/master/Password%20Hashing%20Plugins).
+
+
+
+Download and unzip the repository, or use Git in a terminal with the code below.
+```sh
+git clone https://github.com/fusionauth/fusionauth-contrib.git
+```
+
+Open the file `fusionauth-contrib/Password Hashing Plugins/src/main/java/com/mycompany/fusionauth/plugins/ExampleStytchScryptPasswordEncryptor.java`. The content of the file is shown below.
+
+
+
+This program takes a password and salt and returns a hash. It accepts and returns the `+` form of Base64, but changes it to `-` to work with Java internally. The `factor` parameter is ignored. Scrypt instead has multiple parameters set at the top of the file. If any of these differ from the ones you received in your encrypted CSV file header, you need to change them in the Java file.
+
+There are two other files linked to this scrypt plugin that you shouldn't need to alter:
+- `fusionauth-contrib/Password Hashing Plugins/src/test/java/com/mycompany/fusionauth/plugins/ExampleStytchScryptPasswordEncryptorTest.java`, which holds unit tests for the hasher.
+- `fusionauth-contrib/Password Hashing Plugins/src/main/java/com/mycompany/fusionauth/plugins/guice/MyExampleFusionAuthPluginModule.java`, which makes the hasher file known to FusionAuth.
+
+If you make another hash plugin, you will need to make your own test and add your plugin to the Guice file.
+
+Build the Java plugin and add it to FusionAuth:
+- Open a terminal and run the code below to build a Docker container for Java, with the repository shared as a volume into the container.
+ ```sh
+ cd fusionauth-contrib/.devcontainer
+ docker build -t javaimage .
+ cd ..
+ docker run -it --name javabox -v .:/workspace javaimage
+ ```
+- In the Docker container terminal that is now running, run the code below.
+ ```sh
+ cd "/workspace/Password Hashing Plugins"
+ mvn clean install
+ exit
+ ```
+- If you have Java installed locally, you can run the Maven command without Docker.
+
+All tests should pass, and the plugin file should be available in `Password Hashing Plugins/target/fusionauth-example-password-encryptor-0.1.0.jar`.
+
+### Set Up FusionAuth And Deploy The Plugin
+
+Now copy `fusionauth-example-password-encryptor-0.1.0.jar` to your FusionAuth `plugins` directory and restart FusionAuth.
+
+If you are not already running FusionAuth or want to test this process on another instance, you can start FusionAuth in Docker.
+- Open a new terminal in the `fusionauth-import-scripts` directory and run the code below.
+ ```sh
+ cd stytch/fusionAuthDockerFiles
+ docker compose up
+ ```
+- FusionAuth will now be running and browsable at `http://localhost:9011`. You can log in with `admin@example.com` and `password`. The container is called `fa`.
+- Open a terminal in the `fusionauth-contrib` root directory, where you built the plugin. Run the commands below to copy the JAR file into the FusionAuth container `plugins` directory.
+ ```sh
+ docker exec fa mkdir /usr/local/fusionauth/plugins
+ docker cp "Password Hashing Plugins/target/fusionauth-example-password-encryptor-0.1.0.jar" fa:/usr/local/fusionauth/plugins/fusionauth-example-password-encryptor-0.1.0.jar
+ ```
+- Finally, restart FusionAuth for it to detect the plugin. In the terminal where FusionAuth is running in Docker, press Ctrl + C to stop it, wait, and run `docker compose up` again.
+
+### Save The User Details And Hash To FusionAuth
+
+Now you have the users file `users.json` and the scrypt plugin is installed. To import the users into FusionAuth, you need to run the Node.js import script.
+
+### Use the Script
+
+Open a terminal in the `fusionauth-import-scripts` directory and run the code below.
+```sh
+cd stytch/js
+npm update
+node 4_import.mjs
+```
+
+This import script needs only FusionAuth to be running locally on port 9011 and the `users.json` file to exist in the `fusionauth-import-scripts/stytch/js` directory. The FusionAuth Kickstart file already set a sample application to match the Ids in `4_import.mjs`. If your FusionAuth installation is different, please edit the Ids in `4_import.js`.
+
+
+
+
+
+The `4_import.mjs` script is the code you will need to spend the most time editing if you want to customize your own migration. Let's look at how it works.
+
+The script has two dependencies:
+- `stream-json` — A JSON library that can read massive files with millions of users incrementally. It opens `users.json` for reading in the line `new Chain([fs.createReadStream(filename), parser(), new StreamArray(),])`. For more information, read https://github.com/uhop/stream-json.
+- `@fusionauth/typescript-client` — The FusionAuth SDK for Node.js. It's only used for a single operation: `fa.importUsers(importRequest);`. For more information, read the [FusionAuth Typescript Client Library](/docs/sdks/typescript) documentation.
+
+The main program loop is the code below.
+
+```js
+for await (const { value: stytchUser } of stytchUsers) {
+ const faUser = getFaUserFromStytchUser(stytchUser);
+ await importUser(faUser, stytchUser);
+}
+```
+
+The program takes a Stytch user from the JSON file, maps it to a FusionAuth user ready for import, and sends it to FusionAuth.
+
+The `getFaUserFromStytchUser()` function does a few things:
+- Maps as many matching fields from Stytch to FusionAuth as possible.
+- Takes the first verified contact it can find or the first contact as the primary FusionAuth contact and saves additional contacts in the generic `data` JSON field. This is because FusionAuth allows only one email and phone number while Stytch has an array.
+- Stores all Stytch user details that don't map to FusionAuth in the `data` field.
+- Ignores empty fields and arrays.
+- Uses the name we registered for the hashing algorithm in the Java plugin in `faUser.encryptionScheme = 'example-salted-stytch-scrypt';`.
+
+Carefully read this function and make sure that the user information you need is imported correctly. If any information is not needed, you can comment out the related lines.
+
+The function `getNullOrUUIDFromUserId()` extracts a standard UUID from the Stytch format to set a user Id in FusionAuth.
+
+Finally, this script imports users individually. If this is too slow when running the production migration, wrap the `importUsers()` FusionAuth SDK call in a loop that bundles users in batches of 1000.
+
+If you are uncertain what a user attribute in FusionAuth does, read more in the [user guide](/docs/apis/users), as linked in the [general migration guide](/docs/lifecycle/migrate-users/general-migration) recommended earlier.
+
+
+### Import Roles
+
+Stytch does not support user roles and applications as native properties of users. Instead, you can [implement roles](https://stytch.com/docs/guides/authorization/rbac) by adding them as values in the JSON `trusted_metadata` field of the user. This field is similar to the FusionAuth `data` field.
+
+As discussed in the general migration guide, roles in FusionAuth are stored in a Registration object (a link between a User and an Application).
+
+If you have roles in Stytch you want to import, add them to the `faUser.registrations` array in the import script. This is not included in the tutorial script because every Stytch client will have their own way of linking users and applications in Stytch using JSON.
+
+### Verify the Import
+
+If the migration script ran successfully, you should be able to log in to FusionAuth with one of the imported users:
+- Browse to `http://localhost:9011`.
+- Enter username `user1@example.com` and password `averylongandunguessablepasswordwithlotsofrandominfooofisjoafasnr;,n1`.
+- Logging in should work, but your user will not be able to see anything as it has no administration rights.
+- Log out again and log in with `admin@example.com`.
+- Browse to Users and edit one of the imported users.
+- In the Source tab, you can see all the user details.
+
+#### Debug With The FusionAuth Database
+
+If you have errors logging in, use the FusionAuth database directly to see if your users were imported, and check their hashes manually.
+
+You can use any PostgreSQL browser. [DBeaver](https://dbeaver.io/download) is free, cross-platform, and open source. The connection details are in `fusionauth-import-scripts/stytch/fusionAuthDockerFiles/docker-compose.yml` and `.env`.
+
+In DBeaver or your database IDE, create a new PostgreSQL connection with the following details:
+- URL: `jdbc:postgresql://localhost:6432/fusionauth`
+- Host: `localhost`
+- Port: `6432`
+- Database: `fusionauth`
+- Username: `fusionauth`
+- Password: `hkaLBM3RVnyYeYeqE3WI1w2e4Avpy0Wd5O3s3`
+
+Log in to the database and browse to `Databases/fusionauth/Schemas/public/Tables/identities` and `users`. These two tables will show you the login credentials and user personal information.
+
+## What to Do Next
+
+
+
+## Additional Support
+
+
+
diff --git a/astro/src/content/docs/lifecycle/migrate-users/provider-specific/supabase.mdx b/astro/src/content/docs/lifecycle/migrate-users/provider-specific/supabase.mdx
index c5d1ba2f08..1cbdc31950 100644
--- a/astro/src/content/docs/lifecycle/migrate-users/provider-specific/supabase.mdx
+++ b/astro/src/content/docs/lifecycle/migrate-users/provider-specific/supabase.mdx
@@ -55,7 +55,7 @@ You can write a custom psql script in the Supabase SQL Editor to export user dat
### Other Entities
-
+
* In Supabase, sign-in providers are a source of data for users. FusionAuth calls these Identity Providers.
* Projects are a high-level construct that groups entities such as users and applications together. FusionAuth calls these Tenants.
@@ -175,7 +175,7 @@ Next up, import the user data. Here are the steps you need to take.
### Get the Script
-
+
### Install Needed Gems
diff --git a/astro/src/content/docs/lifecycle/migrate-users/provider-specific/wordpress.mdx b/astro/src/content/docs/lifecycle/migrate-users/provider-specific/wordpress.mdx
new file mode 100644
index 0000000000..08ee49a8af
--- /dev/null
+++ b/astro/src/content/docs/lifecycle/migrate-users/provider-specific/wordpress.mdx
@@ -0,0 +1,460 @@
+---
+title: Migration From WordPress
+description: How to migrate your users from WordPress to FusionAuth.
+section: lifecycle
+subcategory: migrate users
+tertcategory: provider specific
+prerequisites: Docker, Node
+technology: WordPress
+hashTechnology: WordPress Phpass
+importCodeRoot: https://raw.githubusercontent.com/FusionAuth/fusionauth-import-scripts/master
+javaCodeRoot: https://raw.githubusercontent.com/FusionAuth/fusionauth-contrib/master
+---
+
+import InlineUIElement from 'src/components/InlineUIElement.astro';
+import InlineField from 'src/components/InlineField.astro';
+import Aside from '/src/components/Aside.astro';
+import {RemoteCode} from '@fusionauth/astro-components';
+import Identifiers from 'src/content/docs/lifecycle/migrate-users/provider-specific/_identifiers.mdx';
+import MappingUserAttributes from 'src/content/docs/lifecycle/migrate-users/provider-specific/_mapping-user-attributes.mdx';
+import SocialLoginNote from 'src/content/docs/lifecycle/migrate-users/provider-specific/_social-login-note.mdx';
+import SocialLoginMigration from 'src/content/docs/lifecycle/migrate-users/provider-specific/_social-login-migration.mdx';
+import OtherEntitiesIntro from 'src/content/docs/lifecycle/migrate-users/provider-specific/_other-entities-intro.mdx';
+import ScrollRef from 'src/components/ScrollRef.astro';
+import WhatNext from 'src/content/docs/lifecycle/migrate-users/provider-specific/_what-next.mdx';
+import AdditionalSupport from 'src/content/docs/lifecycle/migrate-users/provider-specific/_additional-support.mdx';
+
+export const migration_source_name = 'WordPress';
+export const migration_source_dir = 'wordpress';
+export const script_supports_social_logins = 'false';
+
+## Overview
+
+This document will help you migrate users from {frontmatter.technology} to FusionAuth.
+
+This guide is a low-level, technical tutorial focusing on transferring password hashes, calling APIs, and preparing data when migrating users from {frontmatter.technology}. To understand how to plan a migration at a higher level, please read the [FusionAuth migration guide](/docs/lifecycle/migrate-users/general-migration).
+
+## Prerequisites
+
+If you want to import user passwords in addition to user personal details, you need a basic understanding of how password hashing and salts work. FusionAuth has a [hashing article](/articles/security/math-of-password-hashing-algorithms-entropy) that is a good starting point.
+
+To follow this tutorial, you need:
+- [Node.js](https://nodejs.org/en) to run the migration scripts, and npm.
+- [FusionAuth](/download). The easiest way to run it locally is to use [Docker](https://docs.docker.com/get-docker/) with the configuration file provided later in this tutorial.
+
+## Planning Considerations
+
+### Obtaining User Data
+
+{frontmatter.technology} stores all users in a MySQL database. You will need access to the database to run a SQL extraction script.
+
+### Mapping User Attributes
+
+
+
+### Social Logins
+
+
+
+
+
+### Other Entities
+
+
+
+#### Identifiers
+
+When creating an object with the FusionAuth API, you can specify the Id. It must be a [UUID](/docs/reference/data-types#uuids).
+
+This works for users, applications, and tenants, among others.
+
+## Export Users
+
+In this section, you create a sample WordPress database in Docker, understand where the user details are kept in the database, and export them with a SQL script.
+
+### Start WordPress
+
+Create a directory for this project. In the directory, create a file called `docker-compose.yaml` and insert the code below.
+
+```yaml
+# Source: https://hub.docker.com/r/bitnami/wordpress-nginx
+version: '2'
+services:
+ mariadb:
+ image: docker.io/bitnami/mariadb:11.2
+ container_name: fawp_db
+ ports:
+ - '3306:3306'
+ volumes:
+ - './db:/bitnami/mariadb'
+ environment:
+ # ALLOW_EMPTY_PASSWORD is recommended only for development.
+ - ALLOW_EMPTY_PASSWORD=yes
+ - MARIADB_USER=wp
+ - MARIADB_DATABASE=wp
+ wordpress:
+ image: docker.io/bitnami/wordpress-nginx:6
+ container_name: fawp
+ ports:
+ - '80:8080'
+ - '443:8443'
+ volumes:
+ - './wp:/bitnami/wordpress'
+ depends_on:
+ - mariadb
+ environment:
+ # ALLOW_EMPTY_PASSWORD is recommended only for development.
+ - ALLOW_EMPTY_PASSWORD=yes
+ - WORDPRESS_DATABASE_HOST=mariadb
+ - WORDPRESS_DATABASE_PORT_NUMBER=3306
+ - WORDPRESS_DATABASE_USER=wp
+ - WORDPRESS_DATABASE_NAME=wp
+volumes:
+ mariadb_data:
+ driver: local
+ wordpress_data:
+ driver: local
+```
+
+Open a terminal in the same directory as the file and run the code below to start a MariaDB and WordPress container.
+
+```sh
+mkdir db && mkdir wp && sudo chmod -R 777 ./db ./wp
+docker compose up
+```
+
+This command creates two directories to hold the data for your WordPress and database files, shared with volumes in the Docker containers.
+
+Browse to http://localhost/wp-login.php. Log in with username `user` and password `bitnami`. This is the default WordPress administrator configured in the Bitnami Docker image we used.
+
+### Add A New User
+
+If you use WordPress as a user management system, you need to allow users to register themselves. To do this, browse to **Settings —> General —> Membership**. Enable Anyone can register and click Save Changes.
+
+Log out at the top right of the page. Browse to http://localhost/wp-login.php.
+Register a new user. Enter a username and email `richard@example.com`.
+
+### Explore The Users In The Database
+
+Browse the database in any database IDE that can connect to MariaDB. [DBeaver](https://dbeaver.io/download) is a free, cross-platform IDE you can use.
+
+Create a new MariaDB connection to `localhost`, port `3306`, database `wp`, username `wp`, with no password needed. Open the connection and expand the database tables.
+
+WordPress has two tables related to users: `wp_users` and `wp_usermeta`.
+
+#### The `wp_users` Table
+
+In `wp_users`, you will have data similar to the example below. This table holds identifiers and a password hash for each user.
+
+ID|user_login|user_pass |user_nicename|user_email |user_url |user_registered |user_activation_key |user_status|display_name|
+---|----------|----------------------------------|-------------|----------------|----------------|-----------------------|---------------------------------------------|-----------|------------
+ 1|user |\$P$BVrdsW/NUuXDi0Od0uUdk2SnJHHmQ01|user |user@example.com|http://127.0.0.1|2024-02-21 07:09:20.000| | 0|user |
+ 2|richard |\$P$B/kCzTMDV7ccClaRShJPz8suWQdKc5/|richard |richard@example.com | |2024-02-21 10:52:53.000|1708512773:\$P$BYELgLl.oz9lv.YRNp7ppBA1GxzOEY0| 0|a |
+
+Unfortunately, neither the [WordPress Codex](https://codex.wordpress.org/Database_Description) nor the [WordPress Developer Handbook](https://developer.wordpress.org) gives definitions of the database fields and their meanings. To be certain of their purpose, you would need to read the WordPress source code, but the column definitions below seem obvious enough to trust.
+
+- ID: A unique identifier for each user. It is an auto-incremented integer and serves as the primary key for the table.
+- user_login: The user's username used for logging in to WordPress.
+- user_pass: The hashed password for the user.
+- user_nicename: A URL-friendly version of the user's username. It is used in the author slug URL.
+- user_email: The user's email address. It is unique for each user and used for account management and notifications.
+- user_url: The URL of the user's website. This field is optional and can be left blank.
+- user_registered: The date and time when the user registered on the WordPress site. It is stored in the 'YYYY-MM-DD HH:MM:SS' format.
+- user_activation_key: Used for storing a hash that is used to activate the user's account or for password reset purposes.
+- user_status: This field is deprecated and not used by core WordPress functions. It was originally intended for user status but is now typically left at its default value of 0.
+- display_name: The publicly displayed name of the user. This name is shown on the site in places like author posts or comments.
+
+#### The `wp_usermeta` Table
+
+The `wp_usermeta` table holds all information about a user besides what is kept in the `wp_users` table. WordPress plugins can add their own data, too. Below is the data you should have in `wp_usermeta`.
+
+umeta_id|user_id|meta_key |meta_value
+--------|-------|-------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+1| 1|nickname |user
+2| 1|first_name |UserName
+3| 1|last_name |LastName
+4| 1|description |
+5| 1|rich_editing |true
+6| 1|syntax_highlighting |true
+7| 1|comment_shortcuts |false
+8| 1|admin_color |fresh
+9| 1|use_ssl |0
+10| 1|show_admin_bar_front |true
+11| 1|locale |
+12| 1|wp_capabilities |`a:1:{s:13:"administrator";b:1;}`
+13| 1|wp_user_level |10
+14| 1|dismissed_wp_pointers |
+15| 1|show_welcome_panel |1
+16| 1|session_tokens |`a:1:{s:64:"ed6949d03244303edfd56de57f46b77a7300c2c62406ccde3b717b700c4fce4a";a:4:{s:10:"expiration";i:1708684580;s:2:"ip";s:10:"172.25.0.1";s:2:"ua";s:101:"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.`
+17| 1|wp_dashboard_quick_press_last_post_id|4
+18| 1|community-events-location |`a:1:{s:2:"ip";s:10:"172.25.0.0";}`
+19| 2|nickname |richard
+20| 2|first_name |
+21| 2|last_name |
+22| 2|description |
+23| 2|rich_editing |true
+24| 2|syntax_highlighting |true
+25| 2|comment_shortcuts |false
+26| 2|admin_color |fresh
+27| 2|use_ssl |0
+28| 2|show_admin_bar_front |true
+29| 2|locale |
+30| 2|wp_capabilities |`a:1:{s:10:"subscriber";b:1;}`
+31| 2|wp_user_level |0
+32| 2|default_password_nag |1
+
+The column definitions for the table are:
+
+- umeta_id: A unique identifier for each meta entry. It is an auto-incremented integer and serves as the primary key for the table.
+- user_id: The Id of the user the meta entry belongs to. It links to the Id column in the `wp_users` table.
+- meta_key: The name of the metadata field. WordPress uses this field to store various pieces of user-related information, such as administrative capabilities (`wp_capabilities`), user roles (`wp_user_level`), and custom user meta added by themes or plugins.
+- meta_value: The value of the metadata field. The information here can vary widely depending on the meta_key. This field can store serialized arrays or strings, depending on the type of data being stored.
+
+The `wp_usermeta` table uses an entity-attribute-value (EAV) model. Instead of having one column for each type of field to store for a user, new fields are added using key-value combinations.
+
+Some things to note when deciding which fields you want to import to FusionAuth:
+- Blank fields, like description, can be ignored.
+- Fields that are specific to WordPress, like rich_editing, can be ignored.
+- Some fields are PHP objects serialized into strings, like `wp_capabilities` with value `a:1:{s:13:"administrator";b:1;}`. This reads as: An array of one element containing a string of 13 elements and a boolean of value true. You will need to deserialize these when importing them.
+- Legacy fields, like `wp_user_level`, can be ignored.
+
+Here are some of the `meta_key` types you might find:
+
+- wp_capabilities: Stores the roles assigned to a user in a serialized array. This determines what the user can and cannot do in WordPress.
+- wp_user_level: A legacy way to define user roles and capabilities, primarily kept for backward compatibility. It represents the user's role as a numeric level.
+- rich_editing: Indicates whether the user prefers to use the visual rich editor when writing. The value is either "true" or "false".
+- syntax_highlighting: Indicates whether the user prefers to have syntax highlighting enabled in the code editor. The value is either "true" or "false".
+- comment_shortcuts: Indicates whether the user wishes to use keyboard shortcuts for comment moderation. The value is either "true" or "false".
+- admin_color: The color scheme the user has chosen for the admin dashboard.
+- use_ssl: Whether the user prefers to use SSL when logging into the admin area. The value is either "0" (do not force SSL) or "1" (force SSL).
+- show_admin_bar_front: Determines whether the WordPress admin bar should be displayed to the user when viewing the site. The value is either "true" or "false".
+- locale: The user's preferred language locale for the admin dashboard.
+- wp_dashboard_quick_press_last_post_id: The Id of the last post created using the "Quick Draft" dashboard widget.
+- dismissed_wp_pointers: Tracks the admin pointers dismissed by the user to avoid showing them again.
+- session_tokens: Stores the user's session tokens for maintaining login sessions across different devices.
+- wp_user-settings and wp_user-settings-time: Store user-specific settings for the admin area, such as the true or false status of checkboxes and the timestamp of the last settings update, respectively.
+
+### Export The Users In SQL
+
+FusionAuth provides export and import scripts under a permissive open-source license. To get the scripts, clone the Git repository.
+
+```sh
+git clone https://github.com/FusionAuth/fusionauth-import-scripts
+```
+
+The `wordpress` directory contains all the scripts you need for this tutorial, and `wordpress/exampleData` has the output of the scripts.
+
+Navigate to the `wordpress/src` directory.
+
+```sh
+cd fusionauth-import-scripts/wordpress/src
+```
+
+Install the required npm dependencies by running the following command.
+
+```sh
+npm install
+```
+
+Run the SQL script `1_exportWordpressUsers.sql` in the `fusionauth-import-scripts/wordpress/src` directory against your WordPress database to export all the user data as JSON. View the output as text in DBeaver and save the `json_result` column to a file called `users.txt`.
+
+
+
+The script exports the `wp_users` table as JSON with all `wp_usermeta` fields related to each user as an array called `meta`.
+
+Your `users.txt` file should now have user data similar to the one in `fusionauth-import-scripts/exampleData/1_wordpressExport/users.txt`. Each row is JSON, but the file as a whole is not JSON. Each row must end with a comma and the file contents must be wrapped in brackets.
+
+Copy your `users.txt` file into `fusionauth-import-scripts/wordpress/src` and run the code below.
+
+```sh
+node 2_convertTextToJson.mjs
+```
+
+Your output should be valid JSON and look like the file `fusionauth-import-scripts/exampleData/1_wordpressExport/users.json`.
+
+## Import Users
+
+Now that the user details have been extracted from {frontmatter.technology}, you need to add a password hashing plugin to FusionAuth that supports the phpass hashing algorithm used in {frontmatter.technology}, map the {frontmatter.technology} fields to FusionAuth fields, and then import the users using a script.
+
+### Use A WordPress Password Hash Plugin For FusionAuth
+
+The {frontmatter.hashTechnology} hashing algorithm is not [natively supported by FusionAuth](/docs/reference/password-hashes). However, FusionAuth allows [custom plugins](/docs/extend/code/password-hashes/custom-password-hashing). There is a {frontmatter.hashTechnology} plugin accompanying this article [in this GitHub repository](https://github.com/FusionAuth/fusionauth-contrib/tree/master/Password%20Hashing%20Plugins).
+
+
+
+Download and unzip the repository, or use Git in a terminal with the command below.
+
+```sh
+git clone https://github.com/fusionauth/fusionauth-contrib.git
+```
+
+Open the file `fusionauth-contrib/Password Hashing Plugins/src/main/java/com/mycompany/fusionauth/plugins/ExampleWordPressPhpassPasswordEncryptor.java`. The content of the file is shown below.
+
+
+
+Most of the other hashing plugins in this project take a plaintext password and Base64 salt and return a Base64 hash. Phpass is more complicated. The salt is stored inside the hash, as is the hashing algorithm name. Thus the `encrypt` function needs to perform the password hash comparison inside the phpass algorithm to verify the user's password is correct. The {frontmatter.technology} user import script you will use shortly saves the existing hash to the salt field. If the `checkPassword` function succeeds, then the `encrypt` function returns the existing salt. If it fails, the function returns a random number to ensure that the passwords will never match. Only if given a new password with no existing hash will `encrypt` return a new hash.
+
+The `factor` parameter should always be 8.
+
+There are two other files linked to this plugin that you shouldn't need to alter:
+- `fusionauth-contrib/Password Hashing Plugins/src/main/java/com/mycompany/fusionauth/plugins/ExampleWordPressPhpassPasswordEncryptorTest.java`, which holds unit tests for the hasher.
+- `fusionauth-contrib/Password Hashing Plugins/src/main/java/com/mycompany/fusionauth/plugins/guice/MyExampleFusionAuthPluginModule.java`, which makes the hasher file known to FusionAuth.
+
+If you make another hash plugin, you will need to make your own test and add your plugin to the Guice file. This might be the case if your {frontmatter.technology} installation has overridden the default hashing algorithm.
+
+
+
+Build the Java plugin and add it to FusionAuth:
+
+- Open a terminal in the `fusionauth-contrib` directory and run the commands below to build a Docker container for Java, with the repository shared as a volume into the container.
+
+ ```sh
+ cd .devcontainer
+ docker build -t javaimage .
+ cd ..
+ docker run -it --name javabox -v .:/workspace javaimage
+ ```
+- In the Docker container terminal that is now running, run the commands below.
+ ```sh
+ cd "/workspace/Password Hashing Plugins"
+ mvn clean install
+ exit
+ ```
+- If you have Java installed locally, you can run the Maven command without Docker.
+
+All tests should pass, and the plugin file should be available in `Password Hashing Plugins/target/fusionauth-example-password-encryptor-0.1.0.jar`.
+
+### Set Up FusionAuth And Deploy The Plugin
+
+Now copy `fusionauth-example-password-encryptor-0.1.0.jar` to your FusionAuth `plugins` directory and restart FusionAuth.
+
+If you are not already running FusionAuth or want to test this process on another instance, you can start FusionAuth in Docker.
+- Open a new terminal in the `fusionauth-import-scripts` directory and run the code below.
+ ```sh
+ cd wordpress/fusionAuthDockerFiles
+ docker compose up
+ ```
+- FusionAuth will now be running and browsable at `http://localhost:9011`. You can log in to the [FusionAuth admin UI](http://localhost:9011/admin) with `admin@example.com` and `password`. The container is called `fa`.
+- In the `fusionauth-contrib` terminal where you built the plugin, run the commands below to copy the JAR file into the FusionAuth container `plugins` directory.
+ ```sh
+ docker exec fa mkdir /usr/local/fusionauth/plugins
+ docker cp "Password Hashing Plugins/target/fusionauth-example-password-encryptor-0.1.0.jar" fa:/usr/local/fusionauth/plugins/fusionauth-example-password-encryptor-0.1.0.jar
+ ```
+- Finally, restart FusionAuth for it to detect the plugin. In the terminal where FusionAuth is running in Docker, press Ctrl + C to stop it, wait, and run `docker compose up` again.
+
+### Create Roles For Your Users
+
+The two users you exported have the roles Administrator and Subscriber, as seen in the `wp_capabilities` meta field. Create the roles in FusionAuth. In a terminal in the `fusionauth-import-scripts/wordpress/src` directory, run the code below.
+
+```sh
+node 3_addRoles.mjs
+```
+
+Now in the [FusionAuth admin interface](http://localhost:9011/admin), if you browse to **Applications —> WordpressTestImportApp** and click the Manage Roles button, you can see the roles have been added for your sample Application. This Application was created using a bootstrapping feature of FusionAuth called [Kickstart](/docs/get-started/download-and-install/development/kickstart), defined in `fusionauth-import-scripts/wordpress/fusionAuthDockerFiles/kickstart/kickstart.json`. When FusionAuth comes up for the first time, it will look at the `kickstart.json` file and configure FusionAuth to the specified state.
+
+### Prepare Users For Import To FusionAuth
+
+The next script, `4_convertWpUserToFaUser.mjs`, is the most important. It maps the fields of `users.json` to FusionAuth fields. You may wish to alter this script to change which fields are ignored, or where they are mapped.
+
+The script uses `stream-json`, a JSON library that can incrementally read massive files with millions of users. It opens the `users.json` file for reading in the line `new Chain([fs.createReadStream(inputFilename), parser(), new StreamArray(),]);`. For more information, read https://github.com/uhop/stream-json. The `processUsers()` function calls `getFaUserFromUser()` to map the {frontmatter.technology} user to FusionAuth, and then saves them to an `faUsers.json` file.
+
+The `getFaUserFromUser()` function does a few things:
+- Maps as many matching fields from {frontmatter.technology} to FusionAuth as possible.
+- Stores all {frontmatter.technology} user details that don't map to FusionAuth in the `data` field. If there are details you want to include or exclude, alter the `dataToIgnore` list at the top of the file.
+- Ignores empty fields and arrays.
+- Uses the name we registered for the hashing algorithm in the Java plugin in `faUser.encryptionScheme = 'example-wordpress-phpass';`.
+- Adds Registrations (a Role link between a User and an Application) for users. You will need to change these Ids to match those of your application when doing a real migration.
+
+There are a few helper functions at the bottom of the file to extract keys and values from the {frontmatter.technology} `wp_usermeta` object and deserialize them from PHP. These are used when working with `wp_capabilities`.
+
+Carefully read this function and make sure that the user information you need is imported correctly. If any information is not needed, you can comment out the related lines.
+
+If you are uncertain about what a user attribute in FusionAuth does, read more in the [user guide](/docs/apis/users), as linked in the [general migration guide](/docs/lifecycle/migrate-users/general-migration) recommended earlier.
+
+In a terminal in the `fusionauth-import-scripts/wordpress/src` directory, run the script with the following command.
+
+```sh
+node 4_convertWpUserToFaUser.mjs
+```
+
+### Use The Script
+
+Now you have the users file `faUsers.json` and the {frontmatter.hashTechnology} plugin is installed. To import the users into FusionAuth, you need to run the Node.js import script.
+
+In a terminal in the `fusionauth-import-scripts/wordpress/src` directory, run the command below.
+
+```sh
+node 5_import.mjs
+```
+
+This script uses the FusionAuth SDK for Node.js `@fusionauth/typescript-client`. It's used only for a single operation, `fa.importUsers(importRequest)`. For more information, read the [FusionAuth Typescript Client Library](/docs/sdks/typescript) documentation.
+
+Finally, this script imports users individually. If this is too slow when running the production migration, wrap the `importUsers()` FusionAuth SDK call in a loop that bundles users in batches of 1000.
+
+### Verify The Import
+
+If the migration script ran successfully, you should be able to log in to the `WordpressTestImportApp` application with one of the imported users. In the [FusionAuth admin UI](http://localhost:9011/admin), navigate to **Applications —> WordpressTestImportApp**. Click the View button (green magnifying glass) next to the application and note the OAuth IdP login URL.
+
+
+
+Copy this URL and open it in a new incognito browser window. (If you don’t use an incognito window, the admin user session will interfere with the test.) You should see the login screen. Enter username `user@example.com` and password `bitnami`. Login should work.
+
+
+
+
+Next, log in to the [FusionAuth admin UI](http://localhost:9011/admin) with `admin@example.com` and password `password`. Review the user entries to ensure the data was correctly imported.
+
+
+
+You can manage a user by clicking on the Manage button (black button) to the right of the user in the list of users to review the details of the imported user’s profile. In the Source tab, you can see all the user details as a JSON object.
+
+#### Debug With The FusionAuth Database
+
+If you have errors logging in, you can use the FusionAuth database directly to see if your users were imported, and check their hashes manually.
+
+You can use any PostgreSQL browser. DBeaver will work. The connection details are in `fusionauth-import-scripts/wordpress/fusionAuthDockerFiles/docker-compose.yml` and `.env`.
+
+In your database IDE, create a new PostgreSQL connection with the following details:
+
+- URL: `jdbc:postgresql://localhost:5432/fusionauth`
+- Host: `localhost`
+- Port: `5432`
+- Database: `fusionauth`
+- Username: `fusionauth`
+- Password: `hkaLBM3RVnyYeYeqE3WI1w2e4Avpy0Wd5O3s3`
+
+Log in to the database and browse to `Databases/fusionauth/Schemas/public/Tables`. Tables `identities` and `users` will show you the login credentials and user personal information.
+
+## What To Do Next
+
+{frontmatter.hashTechnology} uses a relatively old and weak hashing algorithm, though not terrible. You might want to rehash your users' passwords on their next login with a stronger algorithm. To enable this setting, follow these [instructions](/docs/extend/code/password-hashes/custom-password-hashing#rehashing-user-passwords).
+
+
+
+## Additional Support
+
+
diff --git a/astro/src/content/docs/operate/deploy/_theme-environment-management.mdx b/astro/src/content/docs/operate/deploy/_theme-environment-management.mdx
index 8c27dac4d4..3dfedfabdc 100644
--- a/astro/src/content/docs/operate/deploy/_theme-environment-management.mdx
+++ b/astro/src/content/docs/operate/deploy/_theme-environment-management.mdx
@@ -1,10 +1,16 @@
+import Aside from 'src/components/Aside.astro';
+
When moving themes from one environment to another, the theme logic and look and feel may be the same, but the assets may be different. FusionAuth templates only have access to [the documented variables](/docs/customize/look-and-feel/template-variables). FusionAuth does not resolve environment variables in the themes templates.
You have a few options to handle the difference in assets between theme environments.
You can set variables at theme build time in the [Helpers](/docs/customize/look-and-feel/helpers) template using the [`assign`](https://freemarker.apache.org/docs/ref_directive_assign.html) directive. These variables can then be used in other templates. You can use a templating language like jinja to build the `Helpers` template at build time.
-Another option is to use custom fields in the `tenant.data` field. The `tenant` object is available to every template. Then you can reference these variables for different hostnames.
+Another option is to use custom fields in the `tenant.data` field. The `tenant` object is available to every template. Then you can reference these variables for different hostnames.
+
+
For example, if your development assets are at `dev.example.com/assets` and your production assets are at `prod.example.com/assets`, set a variable such as `asset_base_url` in the `tenant.data` field.
diff --git a/astro/src/content/docs/operate/deploy/upgrade.mdx b/astro/src/content/docs/operate/deploy/upgrade.mdx
index 16a5eeb6da..5bc7ae4867 100644
--- a/astro/src/content/docs/operate/deploy/upgrade.mdx
+++ b/astro/src/content/docs/operate/deploy/upgrade.mdx
@@ -117,7 +117,8 @@ Configuration management options include:
You can read more in [the Configuration Management guide](/docs/operate/deploy/configuration-management).
-{/* this has a hardcoded header */}
+## Updating Your Theme
+
## Upgrade Options
diff --git a/astro/src/content/docs/release-notes/__database-migration-warning.mdx b/astro/src/content/docs/release-notes/__database-migration-warning.mdx
index 45a9b362f7..bfc50c7080 100644
--- a/astro/src/content/docs/release-notes/__database-migration-warning.mdx
+++ b/astro/src/content/docs/release-notes/__database-migration-warning.mdx
@@ -1,7 +1,7 @@
import Aside from 'src/components/Aside.astro';
+
+
## Next Steps
diff --git a/astro/src/content/quickstarts/quickstart-javascript-remix-web.mdx b/astro/src/content/quickstarts/quickstart-javascript-remix-web.mdx
index 40a1b79228..71517a4bd1 100644
--- a/astro/src/content/quickstarts/quickstart-javascript-remix-web.mdx
+++ b/astro/src/content/quickstarts/quickstart-javascript-remix-web.mdx
@@ -19,6 +19,7 @@ import Intro from '/src/components/quickstarts/Intro.astro';
import LoginArchitectureWeb from '/src/components/quickstarts/LoginArchitectureWeb.astro';
import NextSteps from '/src/components/quickstarts/NextSteps.astro';
import {RemoteCode} from '@fusionauth/astro-components';
+import QuickstartTshirtCTA from '/src/components/quickstarts/QuickstartTshirtCTA.astro'
@@ -229,6 +230,8 @@ You can now browse to [the app](http://localhost:3000).
Log in using `richard@example.com` and `password`. The change page will allow you to enter a number. Log out and verify that you can't browse to the [account page](http://localhost:3000/account).
+
+
## Next Steps
diff --git a/astro/src/content/quickstarts/quickstart-javascript-vue-web.mdx b/astro/src/content/quickstarts/quickstart-javascript-vue-web.mdx
index 43e72a4bf2..35af2f935e 100644
--- a/astro/src/content/quickstarts/quickstart-javascript-vue-web.mdx
+++ b/astro/src/content/quickstarts/quickstart-javascript-vue-web.mdx
@@ -19,7 +19,7 @@ import Intro from '/src/components/quickstarts/Intro.astro';
import LoginArchitectureSdk from '/src/components/quickstarts/LoginArchitectureSdk.astro';
import NextSteps from '/src/components/quickstarts/NextSteps.astro';
import {RemoteCode} from '@fusionauth/astro-components';
-
+import QuickstartTshirtCTA from '/src/components/quickstarts/QuickstartTshirtCTA.astro'
@@ -146,6 +146,8 @@ You can now open up an incognito window and navigate to http://localhost:5173. Y
The username and password of the `example user` can be found in the FusionAuth via Docker section at the top of this article.
+
+
## Next Steps
diff --git a/astro/src/content/quickstarts/quickstart-php-drupal-web.mdx b/astro/src/content/quickstarts/quickstart-php-drupal-web.mdx
index 93c3171e22..0b0c487877 100644
--- a/astro/src/content/quickstarts/quickstart-php-drupal-web.mdx
+++ b/astro/src/content/quickstarts/quickstart-php-drupal-web.mdx
@@ -21,6 +21,7 @@ import Intro from '/src/components/quickstarts/Intro.astro';
import LoginArchitectureWeb from '/src/components/quickstarts/LoginArchitectureWeb.astro';
import NextSteps from '/src/components/quickstarts/NextSteps.astro';
import {RemoteCode} from '@fusionauth/astro-components';
+import QuickstartTshirtCTA from '/src/components/quickstarts/QuickstartTshirtCTA.astro'
@@ -698,6 +699,8 @@ You can now open the application at http://localhost/user/login.
Log in by clicking the Login with FusionAuth button and using the username `richard@example.com` and password `password`.
+
+
## Next Steps
diff --git a/astro/src/content/quickstarts/quickstart-php-laravel-api.mdx b/astro/src/content/quickstarts/quickstart-php-laravel-api.mdx
index a73f5d5209..e9588bd973 100644
--- a/astro/src/content/quickstarts/quickstart-php-laravel-api.mdx
+++ b/astro/src/content/quickstarts/quickstart-php-laravel-api.mdx
@@ -19,6 +19,7 @@ import Intro from '/src/components/quickstarts/Intro.astro';
import LoginArchitectureApi from '/src/components/quickstarts/LoginArchitectureApi.astro';
import NextStepsApi from '/src/components/quickstarts/NextStepsApi.astro';
import {RemoteCode, RemoteValue} from '@fusionauth/astro-components';
+import QuickstartTshirtCTA from '/src/components/quickstarts/QuickstartTshirtCTA.astro'
+
## Next Steps
diff --git a/astro/src/content/quickstarts/quickstart-php-laravel-web.mdx b/astro/src/content/quickstarts/quickstart-php-laravel-web.mdx
index 74e6f19817..fe4dd07d50 100644
--- a/astro/src/content/quickstarts/quickstart-php-laravel-web.mdx
+++ b/astro/src/content/quickstarts/quickstart-php-laravel-web.mdx
@@ -18,6 +18,7 @@ import Intro from '/src/components/quickstarts/Intro.astro';
import LoginArchitectureWeb from '/src/components/quickstarts/LoginArchitectureWeb.astro';
import NextSteps from '/src/components/quickstarts/NextSteps.astro';
import {RemoteCode} from '@fusionauth/astro-components';
+import QuickstartTshirtCTA from '/src/components/quickstarts/QuickstartTshirtCTA.astro'
@@ -271,6 +272,8 @@ php artisan serve
Browse to [the app](http://localhost:8000) at http://localhost:8000. Log in using `richard@example.com` and `password`. The change page will allow you to enter a number. Log out and verify that you can't browse to the [account page](http://localhost:8000/account).
+
+
## Next Steps
diff --git a/astro/src/content/quickstarts/quickstart-php-web.mdx b/astro/src/content/quickstarts/quickstart-php-web.mdx
index 85dd9315c0..86748de965 100644
--- a/astro/src/content/quickstarts/quickstart-php-web.mdx
+++ b/astro/src/content/quickstarts/quickstart-php-web.mdx
@@ -19,7 +19,7 @@ import Intro from '/src/components/quickstarts/Intro.astro';
import LoginArchitectureWeb from '/src/components/quickstarts/LoginArchitectureWeb.astro';
import NextSteps from '/src/components/quickstarts/NextSteps.astro';
import {RemoteCode, RemoteValue} from '@fusionauth/astro-components';
-
+import QuickstartTshirtCTA from '/src/components/quickstarts/QuickstartTshirtCTA.astro'
@@ -208,6 +208,8 @@ php -S localhost:9012 -t public
Browse to the app at http://localhost:9012. Log in using `richard@example.com` and `password`. The change page allows you to enter a number.
+
+
## Next Steps
diff --git a/astro/src/content/quickstarts/quickstart-python-django-web.mdx b/astro/src/content/quickstarts/quickstart-python-django-web.mdx
index d7a7afef47..3b33d6d0c0 100644
--- a/astro/src/content/quickstarts/quickstart-python-django-web.mdx
+++ b/astro/src/content/quickstarts/quickstart-python-django-web.mdx
@@ -18,7 +18,7 @@ import Intro from '/src/components/quickstarts/Intro.astro';
import LoginArchitectureWeb from '/src/components/quickstarts/LoginArchitectureWeb.astro';
import NextSteps from '/src/components/quickstarts/NextSteps.astro';
import {RemoteCode} from '@fusionauth/astro-components';
-
+import QuickstartTshirtCTA from '/src/components/quickstarts/QuickstartTshirtCTA.astro'
@@ -191,6 +191,8 @@ You can now browse to [the Django app](http://localhost:8000/app).
Log in using `richard@example.com` and `password`. The change page will allow you to enter a number and see the result of the POST. Log out and verify that you can't browse to the [account page](http://localhost:8000/app/account).
+
+
## Next Steps
diff --git a/astro/src/content/quickstarts/quickstart-python-flask-web.mdx b/astro/src/content/quickstarts/quickstart-python-flask-web.mdx
index 798715888b..3647568214 100644
--- a/astro/src/content/quickstarts/quickstart-python-flask-web.mdx
+++ b/astro/src/content/quickstarts/quickstart-python-flask-web.mdx
@@ -18,6 +18,7 @@ import Intro from '/src/components/quickstarts/Intro.astro';
import LoginArchitectureWeb from '/src/components/quickstarts/LoginArchitectureWeb.astro';
import NextSteps from '/src/components/quickstarts/NextSteps.astro';
import {RemoteCode} from '@fusionauth/astro-components';
+import QuickstartTshirtCTA from '/src/components/quickstarts/QuickstartTshirtCTA.astro'
@@ -181,6 +182,8 @@ The last step is to implement logout. When you log a user out of an application,
Click the `Logout` button and watch the browser first go to FusionAuth to log out the user, then return to your home page.
+
+
## Next Steps
diff --git a/astro/src/content/quickstarts/quickstart-react-native.mdx b/astro/src/content/quickstarts/quickstart-react-native.mdx
index d176b8403d..e6f739179a 100644
--- a/astro/src/content/quickstarts/quickstart-react-native.mdx
+++ b/astro/src/content/quickstarts/quickstart-react-native.mdx
@@ -20,7 +20,7 @@ import LoginArchitectureWeb from '/src/components/quickstarts/LoginArchitectureW
import NextSteps from '/src/components/quickstarts/NextSteps.astro';
import {RemoteCode, RemoteValue} from '@fusionauth/astro-components';
import QuickstartNgrok from '../../components/quickstarts/quickstart-ngrok.mdx';
-
+import QuickstartTshirtCTA from '/src/components/quickstarts/QuickstartTshirtCTA.astro'
@@ -168,6 +168,8 @@ Wait a few seconds and Expo will build and install the app in your device. You s
You'll finally arrive at the FusionAuth login screen. Fill in and and click on `Submit` to be redirected back to the logged-in ChangeBank page.
+
+
## Next Steps
diff --git a/astro/src/content/quickstarts/quickstart-ruby-on-rails-api.mdx b/astro/src/content/quickstarts/quickstart-ruby-on-rails-api.mdx
index deb204144e..3ee6fd595d 100644
--- a/astro/src/content/quickstarts/quickstart-ruby-on-rails-api.mdx
+++ b/astro/src/content/quickstarts/quickstart-ruby-on-rails-api.mdx
@@ -18,6 +18,7 @@ import Intro from '/src/components/quickstarts/Intro.astro';
import LoginArchitectureApi from '/src/components/quickstarts/LoginArchitectureApi.astro';
import NextStepsApi from '/src/components/quickstarts/NextStepsApi.astro';
import {RemoteCode} from '@fusionauth/astro-components';
+import QuickstartTshirtCTA from '/src/components/quickstarts/QuickstartTshirtCTA.astro'
+
## Next Steps
diff --git a/astro/src/content/quickstarts/quickstart-ruby-rails-web.mdx b/astro/src/content/quickstarts/quickstart-ruby-rails-web.mdx
index da44a4597f..5c23784e31 100644
--- a/astro/src/content/quickstarts/quickstart-ruby-rails-web.mdx
+++ b/astro/src/content/quickstarts/quickstart-ruby-rails-web.mdx
@@ -19,6 +19,7 @@ import Intro from '/src/components/quickstarts/Intro.astro';
import LoginArchitectureWeb from '/src/components/quickstarts/LoginArchitectureWeb.astro';
import NextSteps from '/src/components/quickstarts/NextSteps.astro';
import {RemoteCode, RemoteValue} from '@fusionauth/astro-components';
+import QuickstartTshirtCTA from '/src/components/quickstarts/QuickstartTshirtCTA.astro'
@@ -200,6 +201,8 @@ Start up the Rails application using this command:
You can now open up an incognito window and visit the Rails app at http://localhost:3000 . Log in with the user account you created when setting up FusionAuth, and you’ll see the email of the user next to a logout button.
+
+
## Next Steps
diff --git a/astro/src/content/quickstarts/quickstart-swift-ios-native.mdx b/astro/src/content/quickstarts/quickstart-swift-ios-native.mdx
index 089afd23f8..376fbe7f77 100644
--- a/astro/src/content/quickstarts/quickstart-swift-ios-native.mdx
+++ b/astro/src/content/quickstarts/quickstart-swift-ios-native.mdx
@@ -23,7 +23,7 @@ import Field from '../../components/Field.astro';
import NextSteps from '/src/components/quickstarts/NextSteps.astro';
import {RemoteCode} from '@fusionauth/astro-components';
import QuickstartNgrok from '../../components/quickstarts/quickstart-ngrok.mdx';
-
+import QuickstartTshirtCTA from '/src/components/quickstarts/QuickstartTshirtCTA.astro'
@@ -236,6 +236,8 @@ Now you can run the app on your device. Connect your device to your computer via
Note that you need to enable [developer mode](https://developer.apple.com/documentation/xcode/enabling-developer-mode-on-a-device) on your device to run the app there. You will also need to associate a developer team with your Xcode project. You can do this by selecting the project file in the project navigator and selecting your team in the Signing & Capabilities tab. If you don't have a developer account, you can use your personal Apple ID as the team.
+
+
## Next Steps
diff --git a/astro/src/content/quickstarts/quickstart-wordpress-web.mdx b/astro/src/content/quickstarts/quickstart-wordpress-web.mdx
index 4065e97e48..aadcc4082b 100644
--- a/astro/src/content/quickstarts/quickstart-wordpress-web.mdx
+++ b/astro/src/content/quickstarts/quickstart-wordpress-web.mdx
@@ -22,6 +22,7 @@ import Field from '../../components/Field.astro';
import LoginArchitectureWeb from '/src/components/quickstarts/LoginArchitectureWeb.astro';
import NextSteps from '/src/components/quickstarts/NextSteps.astro';
import {RemoteCode} from '@fusionauth/astro-components';
+import QuickstartTshirtCTA from '/src/components/quickstarts/QuickstartTshirtCTA.astro'
@@ -295,6 +296,8 @@ Each of your previously blank pages now displays the custom HTML and CSS you add
The demonstration application is now complete. Log out. Verify that you can't browse to the [account page](http://localhost:3000/account). Log in using `richard@example.com` and `password`. The make change page will allow you to enter a dollar amount to see how many nickels and pennies you will get.
+
+
## Next Steps
diff --git a/astro/src/css/style.css b/astro/src/css/style.css
index 91e36fb655..21bea84e1e 100644
--- a/astro/src/css/style.css
+++ b/astro/src/css/style.css
@@ -20,3 +20,18 @@ lite-youtube {
color: theme('textColor.green.600');
}
}
+
+/*
+Handle spacing between the title and the code block for markdown code blocks
+ex:
+```json title="stuff"
+{"things": "stuff"}
+```
+*/
+p[data-title] {
+ margin-bottom: 0;
+}
+
+p[data-title] + div pre {
+ margin-top: .25rem;
+}
diff --git a/astro/src/diagrams/docs/get-started/run-in-cloud/github-actions/overview.astro b/astro/src/diagrams/docs/get-started/run-in-cloud/github-actions/overview.astro
index a4139cef12..76232897d8 100644
--- a/astro/src/diagrams/docs/get-started/run-in-cloud/github-actions/overview.astro
+++ b/astro/src/diagrams/docs/get-started/run-in-cloud/github-actions/overview.astro
@@ -16,17 +16,15 @@ graph LR
direction TB
4[FusionAuth Admin UI] --> 5[Database]
end
+
+ subgraph github [GitHub Action Marketplace]
+ direction TB
+ 6[FusionAuth Action]
+ end
+ 3 --> 6
1 --> 4
`;
-
-// add back the following lines when market place action published
-//subgraph github [GitHub Action Marketplace]
-// direction TB
-// 6[FusionAuth Action]
-//end
-
-//3 --> 6
---