Skip to content
Merged

1.64.0 #4164

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
e8cdadf
Add documentation on Key Master Secrets and using them in Lambda func…
kingds Mar 6, 2026
4760468
Merge branch 'main' into release/1.64.0
wied03 Mar 10, 2026
8054ab7
Fix Secret key type naming. Add doc on updating secret value. (#4174)
kingds Mar 11, 2026
35afdbc
Add documentation on modifying page titles in simple theme editor (#4…
kingds Mar 13, 2026
7fa9d0b
ENG-4023 - Maxmind/license related changes (#4172)
wied03 Mar 13, 2026
efbf3a0
Missed DPoP (#4171)
wied03 Mar 10, 2026
c3a25e3
Updated cognito blog post (#4173)
mooreds Mar 10, 2026
dabe4e4
Add permify support blog (#4179)
mooreds Mar 13, 2026
f805654
Update session/logout doc (#4176)
mooreds Mar 14, 2026
481e770
Securing agents article (#4169)
mooreds Mar 15, 2026
47f91db
fixed hook (#4183)
mooreds Mar 15, 2026
f699347
Improve docs landing page (#4184)
nathan-contino Mar 16, 2026
82e3538
Move mermaid styles into style.css because mermaid.css is not getting…
nathan-contino Mar 16, 2026
9681eee
Add release notes v1 and initial draft of blog post
nathan-contino Mar 16, 2026
50ff796
Add S256 code challenge method to openid configuration
nathan-contino Mar 16, 2026
c151975
Mostly complete 1.64 blog post
nathan-contino Mar 17, 2026
a67b25b
Finish off blog post and fix dark theming
nathan-contino Mar 17, 2026
7580529
Make nav menus more consistent with dark theme selector
nathan-contino Mar 17, 2026
930f1a7
Document context parameter for all lambda types (#4187)
kingds Mar 17, 2026
9870e6e
Blog index cleanup
nathan-contino Mar 17, 2026
e125ec4
link fix
nathan-contino Mar 17, 2026
9781b11
Update release date to 3.19
nathan-contino Mar 17, 2026
219cc29
cleanup
nathan-contino Mar 17, 2026
b4ee6b6
ENG-4043 - Passwordless/email strategy config (#4181)
wied03 Mar 17, 2026
cd86941
Update release notes based on dave and andy feedback
nathan-contino Mar 17, 2026
1cfe0fc
Prevent dark mode mermaid diagrams from flashing white as they load; …
nathan-contino Mar 18, 2026
74d94f0
Trim words
nathan-contino Mar 18, 2026
8a66716
Revise code sample
nathan-contino Mar 18, 2026
cb850f6
Add several pieces of flair
nathan-contino Mar 18, 2026
93cc01c
Merge branch 'main' into release/1.64.0
nathan-contino Mar 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions astro/astro.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ const config = defineConfig({
})
],
markdown: {
smartypants: false,
remarkPlugins: [
remarkMdx,
mermaidTitleFix,
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added astro/public/img/blogs/release-1-64/shibe.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion astro/src/components/Head.astro
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const slug = Astro.params.slug;
<script src="/js/Search-0.2.3.js" type="text/javascript"></script>
<script src="/js/ScrollSpy-0.1.0.js" type="text/javascript"></script>
<script src="/js/Tabs-0.1.0.js" type="text/javascript"></script>
{ hasDark && <script src="/js/ThemeSelector-0.1.0.js" type="text/javascript"></script> }
<script src="/js/ThemeSelector-0.1.0.js" type="text/javascript"></script>
<script src="/js/Visibility-0.1.2.js" type="text/javascript"></script>
<script src="/js/BlogScrollTable-0.1.0.js" type="text/javascript"></script>
<title>{title}</title>
Expand Down
4 changes: 1 addition & 3 deletions astro/src/components/JSON.astro
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ const parsedTitle = title ? parseInline(title) : '';
const code = JSON.stringify(json.data, null, 2);
---
{/* title is optional, do not render an empty em tag */}
{title && <p class="mb-0"><em set:html={parsedTitle}/></p>}
{since && <AvailableSince since={since}/>}
<div class="mb-14 prose-pre:mt-1">
{title && <p data-title=`${parsedTitle}`><em set:html={parsedTitle}/></p>}
<Code lang="json" {code}></Code>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface Props {
thanks?: string;
bonusThanks?: string;
category: string;
since?: string;
resolvedIn?: string;
viaIssue?: string;
}
Expand All @@ -18,6 +19,7 @@ const {
issue,
bonusIssue,
thanks,
since,
bonusThanks,
resolvedIn,
viaIssue,
Expand All @@ -38,7 +40,7 @@ const label = getCategoryLabel(category);
{
resolvedIn && viaIssue && (
<p class="markdown-wrapper">
Resolved in version <code>{resolvedIn}</code> via
Resolved in version <a href=`#version-${resolvedIn.replaceAll('.', '-')}`><code>{resolvedIn}</code></a> via
<a
target="_blank"
rel="noopener noreferrer"
Expand All @@ -50,6 +52,8 @@ const label = getCategoryLabel(category);
</p>
)
}
{ since && <p class="thanks-text markdown-wrapper">Since: <a href=`#version-${since.replaceAll('.', '-')}`><code>{since}</code></a></p>
}
{
thanks && (
<p class="thanks-text markdown-wrapper">
Expand Down
11 changes: 9 additions & 2 deletions astro/src/components/nav/BlogNav.astro
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,19 @@ const moreItems = [
{name: 'Download', href: '/download'},
];

// order matters for the buttons
const buttons: {key: string, mobile?: boolean}[] = [
{key: 'divider'},
{key: 'divider'}
];
if (Astro.props.darkModeToggle) {
buttons.push({key: 'dark-mode', mobile: true});
}
buttons.push(
{key: 'search', mobile: true},
{key: 'login'},
{key: 'expert'},
];
);

---
<Nav sectionTitle="Blog"
sectionTitleHref="/blog/"
Expand Down
8 changes: 4 additions & 4 deletions astro/src/components/nav/ComposableNav.astro
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,12 @@ const {sectionTitle, breadcrumbs, sectionTitleHref, categoryItems, moreItems, ca
</div>

<div
class="menu-content-container lg:origin-top-left lg:opacity-0 lg:group-hover:opacity-100 lg:group-hover:scale-100 lg:scale-0 lg:z-10 lg:mr-3 lg:absolute lg:duration-300 menu-content lg:transition lg:bg-white lg:-mt-1 lg:outline-none lg:right-0 lg:rounded-md lg:shadow-xl lg:text-base lg:top-12 lg:w-48 dark:bg-slate-800"
class="menu-content-container lg:origin-top-left lg:opacity-0 lg:group-hover:opacity-100 lg:group-hover:scale-100 lg:scale-0 lg:z-10 lg:mr-3 lg:absolute lg:duration-300 menu-content lg:transition lg:bg-white lg:-mt-1 lg:outline-none lg:right-0 lg:rounded-md lg:shadow-xl lg:text-base lg:top-12 lg:w-48 dark:bg-slate-900"
role="menu" aria-orientation="vertical" aria-labelledby="menu-button" tabindex="-1" id="category-menu">
<ul class="mb-5 ml-4 lg:mb-0 lg:ml-0 lg:space-y-0 lg:flex lg:flex-col">
{categoryItems && categoryItems.map((item ,idx) =>
<li class:list={[
"lg:border-l-0 lg:hover:border-l-0 lg:block lg:font-medium lg:hover:text-orange-400 lg:px-0 dark:text-slate-100 lg:text-slate-700 lg:text-sm",
"lg:border-l-0 lg:hover:border-l-0 lg:block lg:font-medium lg:hover:text-indigo-400 lg:px-0 dark:text-slate-100 lg:text-slate-700 lg:text-sm",
"block border-l-2 border-slate-700 border-solid font-semibold hover:border-indigo-500 hover:border-l-2 hover:border-solid hover:ease-linear hover:text-indigo-500 hover:transition-colors relative text-base text-white"
]}>
<a class:list={[
Expand Down Expand Up @@ -100,12 +100,12 @@ const {sectionTitle, breadcrumbs, sectionTitleHref, categoryItems, moreItems, ca
</div>

<div
class="menu-content-container lg:origin-top-right lg:opacity-0 lg:group-hover:opacity-100 lg:group-hover:scale-100 lg:scale-0 lg:z-10 lg:mr-3 lg:absolute lg:duration-300 menu-content lg:transition lg:bg-white lg:-mt-1 lg:outline-none lg:right-0 lg:rounded-md lg:shadow-xl lg:text-base lg:top-12 lg:w-48 dark:bg-slate-800"
class="menu-content-container lg:origin-top-right lg:opacity-0 lg:group-hover:opacity-100 lg:group-hover:scale-100 lg:scale-0 lg:z-10 lg:mr-3 lg:absolute lg:duration-300 menu-content lg:transition lg:bg-white lg:-mt-1 lg:outline-none lg:right-0 lg:rounded-md lg:shadow-xl lg:text-base lg:top-12 lg:w-48 dark:bg-slate-900"
role="menu" aria-orientation="vertical" aria-labelledby="menu-button" tabindex="-1" id="more-menu">
<ul class="mb-5 ml-4 lg:mb-0 lg:ml-0 lg:space-y-0 lg:flex lg:flex-col">
{moreItems && moreItems.map((item, idx) =>
<li class:list={[
"lg:border-l-0 lg:hover:border-l-0 lg:block lg:font-medium lg:hover:text-orange-400 lg:px-0 dark:text-slate-100 lg:text-slate-700 lg:text-sm",
"lg:border-l-0 lg:hover:border-l-0 lg:block lg:font-medium lg:hover:text-indigo-400 lg:px-0 dark:text-slate-100 lg:text-slate-700 lg:text-sm",
"block border-l-2 border-slate-700 border-solid font-semibold hover:border-indigo-500 hover:border-l-2 hover:border-solid hover:ease-linear hover:text-indigo-500 hover:transition-colors relative text-base text-white"
]}>
<a class:list={[
Expand Down
83 changes: 83 additions & 0 deletions astro/src/content/blog/announcing-fusionauth-1-64.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
---
publish_date: 2026-03-19
title: Announcing FusionAuth 1.64 - The Secret Shibe
description: "FusionAuth 1.64.0 stores secret values in lambdas like a doggo stores rawhide bones in your couch cushions."
authors: Nathan Contino
image: /img/blogs/release-1-64/fusionauth-1-64.png
categories: Release Updates, Product
tags: release notes, bugfixes, lambdas, secrets, oauth2, lambda secrets, magic codes, custom page titles
excerpt_separator: "{/* more */}"
---
import Breadcrumb from 'src/components/Breadcrumb.astro';

Happy zero-th birthday, FusionAuth `1.64.0`! This version includes [Lambda Secrets](/docs/extend/code/lambdas#secrets), dynamic discovery for OAuth endpoints, codes for email passwordless login, and custom page titles for simple themes. As usual, we've also taken the time to quash some long(and short)standing bugs.

We're calling this release the Secret Shibe since this version stores secret values for your lambdas as assuredly as a faithful doggo stores rawhide bones in your couch cushions:

![secret shibe](/img/blogs/release-1-64/shibe.jpg "wow, not dystopian doge")

## Safely Access Secrets from Lambdas

We all have secrets. Now FusionAuth enables you to store and access them securely with [lambda secrets](/docs/extend/code/lambdas#secrets).

1. In the Admin UI, visit <Breadcrumb>Settings -> Key Master</Breadcrumb>.
1. From the dropdown in the upper right, select <Breadcrumb>Import secret</Breadcrumb>.
1. Specify a unique **secret name**, which you will use to access your secret from a lambda.
1. Specify your **secret value** (the value you are securing).
![entering a secret name and secret value in the Admin UI](/img/blogs/release-1-64/create-secret.png "entering a secret name and secret value in the Admin UI")

To access secrets, use the `context` parameter. Pass a secret **name** to `context.services.secrets.get()` to retrieve the corresponding secret **value** as a string:

```javascript
function populate(jwt, user, registration, context) {
// access a secret named 'super-secret-key'
var apiKey = context.services.secrets.get('super-secret-key');

// use that secret as an api credential
var response = fetch("https://api.example.org/api/status?" + user.id, {
method: "GET",
headers: {
"content-type": "application/x-www-form-urlencoded"
"authorization": "Basic " + apiKey
}
});

if (response.status === 200) {
var jsonResponse = JSON.parse(response.body);
console.log('response: ' + jsonResponse);
jwt.status = jsonResponse.status;
} else {
console.warn('failed request: ' + response.status);
jwt.status = "basic";
}
}
```

All right then, keep your secrets.

## Codes for Email Passwordless Login

Previously, [passwordless login](/docs/lifecycle/authenticate-users/passwordless/) via email always used the `ClickableLink` (magic link) option.
But [not everyone likes magic](https://harrypotter.fandom.com/wiki/Petunia_Dursley). Sometimes it's nicer to just get a code. To accommodate this preference, the email login strategy now supports both magic links and the `FormField` option, which provides a passwordless login code.

![Entering a passwordless login code into the FusionAuth login page](/img/docs/lifecycle/authenticate-users/passwordless/passwordless-phone-otp-request-form.png)

To configure the default email login strategy for `/api/passwordless/start`, set `application.passwordlessConfiguration.emailLoginStrategy`. However, you can also specify a method when you call the API.

## Custom Page Titles for Simple Themes

Previously, [Simple Themes](/docs/customize/look-and-feel/simple-theme-editor) didn't include the ability to override the page title. As a result, Simple Theme users would occasionally notice tab names like "Login | FusionAuth". For users who aren't aware of their CIAM provider, this could be confusing:

![tab displaying the default fusionauth page title](/img/blogs/release-1-64/simple-theme-page-title-tab-name.png "tab displaying the default fusionauth page title")

`1.64.0` adds the ability to set custom page titles for Simple Themes. Just add `oauth2-authorize-page-title` to your custom messages, for example:

```
oauth2-authorize-page-title=Even Though I Love FusionAuth I Still Want to Override This Page Title
```

* * *

We also fixed a large number of bugs: for a full list, take a look at changelog entries marked with the "fix" category in the [release notes](/docs/release-notes/#version-1-64-0).

Thanks again for using FusionAuth! We still love you, even if many of you want to hide our name from your page titles.
2 changes: 1 addition & 1 deletion astro/src/content/docs/_shared/_update-key-note.mdx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Only the name of the Key may be changed; all other fields will remain the same. If you need to update a Key with a new certificate, algorithm or other attributes, please [Import a Key](/docs/apis/keys#import-a-key).
For most key types only the name of the Key may be changed. For `Secret` keys, the `key.secret` value may also be updated. If you need to update a Key with a new certificate, algorithm or other attributes, please [Import a Key](/docs/apis/keys#import-a-key).

For example, if you have a Key with an associated expiring certificate, you'll need to follow the steps similar to those outlined in the [JWT signing key rotation documentation](/docs/operate/secure/key-rotation#jwt-signing-key-rotation):

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
[#setting url_escaping_charset="UTF-8"]
You have requested to log into FusionAuth using this email address. If you do not recognize this request please ignore this email.
<p>
[#-- The optional 'state' map provided on the Start Passwordless API call is exposed in the template as 'state' --]
[#assign url = "http://localhost:9011/oauth2/passwordless/${code}?tenantId=${user.tenantId}" /]
[#list state!{} as key, value][#if key != "tenantId" && value??][#assign url = url + "&" + key?url + "=" + value?url/][/#if][/#list]
<a href="${url}">${url}</a>
</p>

[#if oneTimeCode??]
<p>
Login code: ${oneTimeCode}
</p>
[#else]
<p>
[#-- The optional 'state' map provided on the Start Passwordless API call is exposed in the template as 'state' --]
[#assign url = "http://localhost:9011/oauth2/passwordless/${code}?tenantId=${user.tenantId}" /]
[#list state!{} as key, value][#if key != "tenantId" && value??][#assign url = url + "&" + key?url + "=" + value?url/][/#if][/#list]
<a href="${url}">${url}</a>
</p>
[/#if]
- FusionAuth Admin
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
[#setting url_escaping_charset="UTF-8"]
You have requested to log into FusionAuth using this email address. If you do not recognize this request please ignore this email.

[#if oneTimeCode??]
<p>
Login code: ${oneTimeCode}
</p>
[#else]
[#-- The optional 'state' map provided on the Start Passwordless API call is exposed in the template as 'state' --]
[#assign url = "http://localhost:9011/oauth2/passwordless/${code}?tenantId=${user.tenantId}" /]
[#list state!{} as key, value][#if key != "tenantId" && value??][#assign url = url + "&" + key?url + "=" + value?url/][/#if][/#list]
[#assign url = "http://localhost:9011/oauth2/passwordless/${code}?tenantId=${user.tenantId}" /]
[#list state!{} as key, value][#if key != "tenantId" && value??][#assign url = url + "&" + key?url + "=" + value?url/][/#if][/#list]

${url}
${url}

[/#if]
- FusionAuth Admin
12 changes: 12 additions & 0 deletions astro/src/content/docs/apis/_application-request-body.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,18 @@ import Xmlsignaturec14nmethodValues from 'src/content/docs/_shared/_xmlSignature
<APIField name="application.passwordlessConfiguration.enabled" type="Boolean" optional since="1.5.0">
Determines if passwordless login is enabled for this application.
</APIField>
<APIField name="application.passwordlessConfiguration.emailLoginStrategy" type="String" optional since="1.64.0">
The default login strategy by which the user will complete the passwordless login via email. The possible values are:

* `ClickableLink` - send the user a clickable link that they can open in a web browser to authenticate
* `FormField` - send the user a short code that they can enter into a form field to authenticate
</APIField>
<APIField name="application.passwordlessConfiguration.phoneLoginStrategy" type="String" optional since="1.64.0">
The default login strategy by which the user will complete the passwordless login via phone. The possible values are:

* `ClickableLink` - send the user a clickable link that they can open in a web browser to authenticate
* `FormField` - send the user a short code that they can enter into a form field to authenticate
</APIField>
<TenantApplicationPhoneConfiguration base_field_name={"application"} show_feature_blurb={true} is_tenant={false} />
<APIField name="application.registrationConfiguration.birthDate.enabled" type="Boolean" optional since="1.4.0">
Determines if the `birthDate` field will be included on the registration form.
Expand Down
12 changes: 12 additions & 0 deletions astro/src/content/docs/apis/_application-response-body-base.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,18 @@ import Xmlsignaturec14nmethodValues from 'src/content/docs/_shared/_xmlSignature
<APIField name={ props.base_field_name + ".passwordlessConfiguration.enabled" } type="Boolean" since="1.5.0">
Determines if passwordless login is enabled for this application.
</APIField>
<APIField name={ props.base_field_name + ".passwordlessConfiguration.emailLoginStrategy" } type="String" since="1.64.0">
The default login strategy by which the user will complete the passwordless login via email. The possible values are:

* `ClickableLink` - send the user a clickable link that they can open in a web browser to authenticate
* `FormField` - send the user a short code that they can enter into a form field to authenticate
</APIField>
<APIField name={ props.base_field_name + ".passwordlessConfiguration.phoneLoginStrategy" } type="String" since="1.64.0">
The default login strategy by which the user will complete the passwordless login via phone. The possible values are:

* `ClickableLink` - send the user a clickable link that they can open in a web browser to authenticate
* `FormField` - send the user a short code that they can enter into a form field to authenticate
</APIField>
<TenantApplicationPhoneConfiguration base_field_name={props.base_field_name}
application_template_config_override_text={props.application_template_config_override_text}
is_tenant={false}/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,16 @@ import JSON from 'src/components/JSON.astro';
* `HS256` - HMAC using SHA-256 hash algorithm
* `HS384` - HMAC using SHA-384 hash algorithm
* `HS512` - HMAC using SHA-512 hash algorithm
* `None` - Secret <AvailableSince since="1.64.0" />
</APIField>
<APIField name="key.certificate" type="String" optional>
The certificate to import. The `publicKey` will be extracted from the certificate.
</APIField>
<APIField name="key.kid" type="String" optional>
The Key identifier 'kid'. When this value is omitted, one will be generated.
The Key identifier 'kid'. When this value is omitted, one will be generated. For Secrets, the `kid` is derived from `key.name` and cannot be specified separately.
</APIField>
<APIField name="key.name" type="String" required>
The name of the Key. It must be unique among all Keys.
The name of the Key. It must be unique among all Keys. For Secrets, this value is used to derive the `kid`.
</APIField>
<APIField name="key.publicKey" type="String" optional>
The Key public key. If the key is only to be used for signing, only a private key is necessary and this field may be omitted. This field should be omitted when importing an HMAC key type.
Expand All @@ -36,7 +37,7 @@ import JSON from 'src/components/JSON.astro';
The Key private key. If the key is only to be used for signature validation, only a public key is necessary and this field may be omitted. This field should be omitted when importing an HMAC key type.
</APIField>
<APIField name="key.secret" type="String" optional>
The Key secret. This field is required if importing an HMAC key type.
The Key secret. This field is required if importing an HMAC key type or a Secret. Secrets must be 256 characters or less.
</APIField>
<APIField name="key.type" type="String" optional>
The Key type. This field is required if importing an HMAC key type, or if importing a public key / private key pair. The possible values are:
Expand All @@ -45,6 +46,7 @@ import JSON from 'src/components/JSON.astro';
* `OKP` <AvailableSince since="1.62.0" />
* `RSA`
* `HMAC`
* `Secret` <AvailableSince since="1.64.0" />
</APIField>
</APIBlock>

Expand Down
3 changes: 3 additions & 0 deletions astro/src/content/docs/apis/_key-put-request-body.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import JSON from 'src/components/JSON.astro';
<APIField name="key.name" type="String" required>
The name of the Key. It must be unique among all Keys.
</APIField>
<APIField name="key.secret" type="String" optional>
The Key secret. This field may be updated for a `Secret` key type. Secrets must be 256 characters or less.
</APIField>
</APIBlock>

<JSON title="Example Request JSON" src="keys/put-request.json" />
Loading
Loading