Skip to content

Add async registration flow for devices#7820

Open
knolleary wants to merge 6 commits into
mainfrom
7731-asyncsession-api
Open

Add async registration flow for devices#7820
knolleary wants to merge 6 commits into
mainfrom
7731-asyncsession-api

Conversation

@knolleary

Copy link
Copy Markdown
Member

Closes #7731

This creates an async device registration service that allows a Device Agent to direct a user to a URL to get their device registered, and have the OTC registration code get returned to the device.

We don't have the corresponding Device Agent changes (yet), but to test this PR you can simulate it on the command line.

In these examples, I'm assuming you are testing against a forge app running on localhost:3000 - you'll need to adjusted the hostname/port as appropriate.

  1. Start an async registration:

    curl -X POST http://localhost:3000/api/v1/devices/_/register
    
  2. This will return an JSON object with registerUrl and doneUrl. Note they are actually paths rather than full URLs.

  3. Open registerUrl in your browser. It will take you through the device registration process. At the end, it shows you the OTC for the newly registered device.

  4. Poll the doneUrl in the background - it will return 202 status code whilst the registration process is in progress. Once the device has been registered, this url will return 200 with the otc in the payload object. It will only do this once - subsequent requests will 404.

I have added UTs for the backend apis that cover all the interactions with the API. I have not done any UI Test coverage.

There are a couple items outstanding (that may be follow up PRs)

  • billing notification (show the pricing table if the device registration will incur charges)
  • check the full end-to-end "a user has to sign-up first, then gets redirected back to this flow". I have done that a couple times, but need to double check it all.

@knolleary knolleary added the area:migration Involves a database migration label Jul 14, 2026
@knolleary
knolleary requested a review from cstns July 14, 2026 10:58
@knolleary knolleary changed the title 7731 asyncsession api Add async registration flow for devices Jul 14, 2026

@cstns cstns left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking very good overall, just a few minor nitpicks from me. The most important one is the rate limiter on the registration endpoint.

const age = Date.now() - session.createdAt.getTime()
if (age > 1000 * 60 * 30) {
// session is older than 30 minutes, expire it
await session.destroy()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel we should also add a housekeeper task to do a daily cleanup. Some tokens might be created and never used and remain stale.

<div class="ff-layout--plain">
<div class="ff-header">
<!-- Mobile: Icon-only logo -->
<img class="ff-logo lg:hidden" src="/ff-minimal-red.svg" alt="FlowFuse">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be wrapped in an anchor link to enable navigation when clicked.

Image

Found myself in a situation where i couldn't navigate back home without altering the url. Also worth adding a home button under the The registration session you are trying to use is invalid or has expired. Please start the registration process again. notice, similar to our 404 pages

@@ -0,0 +1,261 @@
<template>
<MultiStepForm
ref="multiStepForm"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image I'm unable to navigate through the steps by clicking the progress step indicator

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't blame the guy using your component 😉 . This has always been the case AFAIK, unless there's some secret sauce I've missed.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I vaguely remember adding something that allowed you to block particular steps or all of them, I need to check sure

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a way to disable the top indicators, but a flag check is tripping it up. I'll raise a separate fix for it

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

})
})

describe.only('Async device registration', async function () {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dev leftover

if (this.registrationSession) {
payload.registrationSession = this.registrationSession
}
console.log(payload)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dev leftover

<p>Applications are used to manage and group together your Node-RED Instances and resources.</p>

<div v-if="writableApplications.length > 5" class="search-wrapper flex justify-center my-2">
<div v-if="writableApplications.length > 1" class="search-wrapper flex justify-center my-2">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm trying to understand why this was shown only if we had more than 5 writ-able applications

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was the choice someone made in the original form. Presumably, if there are <6, then they are all visible and quick to pick from the list. Only if there is more does the list become harder to manage and the filter is helpful.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may be guilty of that, looking at it now it seems to be an arbitrary number

name: 'ff-layout-plain',
computed: {
...mapState(useUxStore, ['overlay']),
...mapState(useProductBrokersStore, ['interview']),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need this?

@@ -52,25 +52,6 @@ export default {
...mapState(useUxStore, ['overlay']),
...mapState(useProductBrokersStore, ['interview']),
...mapState(useAccountSettingsStore, ['hasAvailableTeams'])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...mapState(useAccountSettingsStore, ['hasAvailableTeams']) can also be removed as it was only used in the stale block you removed

meta: {
title: 'Register Remote Instance',
layout: 'plain'
// menu: {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should remove this, there's nowhere to go back from, this was a newly opened page

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:migration Involves a database migration

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DA Onboarding: async login service

2 participants