Skip to content

feat: use last_login column to detect first login and redirect onboarding#835

Merged
sejori merged 10 commits intomainfrom
custom-onboarding-redirect
Mar 11, 2026
Merged

feat: use last_login column to detect first login and redirect onboarding#835
sejori merged 10 commits intomainfrom
custom-onboarding-redirect

Conversation

@sejori
Copy link
Contributor

@sejori sejori commented Mar 10, 2026

Changes:

Backend (Rust)

dwctl/src/config.rs — Added onboarding_url: Option<String> to Config struct, defaulting to None. When set, new users are redirected to this URL after their first login.

config.yaml — Added commented-out onboarding_url example for local dev.

dwctl/src/db/models/users.rs — Added last_login to UserDBResponse (was being dropped during conversion). Added acknowledge_login: Option<bool> to UserUpdateDBRequest so the frontend can trigger setting last_login.

dwctl/src/db/handlers/users.rs — Plumbed last_login through the From conversion. Added SQL CASE to the UPDATE query: when acknowledge_login is true, sets last_login = NOW().

dwctl/src/db/handlers/organizations.rs — Added last_login: None to virtual org user constructions.

dwctl/src/api/models/users.rs — Added onboarding_redirect_url: Option<String> to UserResponse (skipped if None). Added acknowledge_login to UserUpdate. Added with_onboarding_redirect_url() builder method. Fixed last_login mapping (was hardcoded to None).

dwctl/src/api/handlers/users.rs — For GET /users/current: if last_login is null and onboarding_url is configured, attaches onboarding_redirect_url to the response.

Test fixtures — Added new fields (onboarding_url, acknowledge_login, onboarding_redirect_url) across test/utils.rs, api_keys.rs, auth.rs, payments.rs, current_user.rs.

Frontend (TypeScript/React)

dashboard/src/api/control-layer/types.ts — Added last_login, onboarding_redirect_url to User interface. Added acknowledge_login to UserUpdateRequest.

dashboard/src/contexts/auth/context.tsx — In checkAuthStatus, after fetching the current user:

  1. Checks sessionStorage to avoid redundant PATCHes within a session
  2. PATCHes acknowledge_login: true to set last_login (prevents future redirects)
  3. Checks for ?redirect= URL param (org invite links take priority)
  4. If first login + no redirect param + onboarding URL present → redirects to onboarding

Deployment

Need to ensure onboarding_url: "https://onboarding.doubleword.ai" for production deployments.

Flow

New user → SSO → Dashboard → GET /users/current
  → last_login: null, onboarding_redirect_url: "https://..."
  → PATCH acknowledge_login: true (sets last_login)
  → Check for ?redirect= param (org invite priority)
  → No redirect param? → window.location.href = onboarding URL
  → On return: last_login is set → no redirect, normal dashboard

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Mar 10, 2026

Deploying control-layer with  Cloudflare Pages  Cloudflare Pages

Latest commit: 2fc94f8
Status: ✅  Deploy successful!
Preview URL: https://c79127b0.control-layer.pages.dev
Branch Preview URL: https://custom-onboarding-redirect.control-layer.pages.dev

View logs

@JoshC8C7
Copy link
Contributor

Why make the frontend patch? we can just have the get on current users set the last login (even if it isnt the most rest-y)

@JoshC8C7
Copy link
Contributor

Could also do this by adding a claim to the token for a first login and having the current_user logic check for it and then redirecting. Or could also use try_proxy_header_auth to put a field into current_user and redirect based on that

@sejori sejori marked this pull request as ready for review March 10, 2026 16:50
Copilot AI review requested due to automatic review settings March 10, 2026 16:50
@sejori
Copy link
Contributor Author

sejori commented Mar 10, 2026

Why make the frontend patch? we can just have the get on current users set the last login (even if it isnt the most rest-y)

We could do but that would update last_login on every call which happens many times in a single user session. You might be right that maybe there is a better way to do it tho. Let me investigate if there is a good auth callback API or something we could use instead.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a first-login onboarding redirect mechanism by using the users.last_login column to detect first login and exposing an onboarding_redirect_url to the dashboard.

Changes:

  • Backend: plumbs last_login through DB/API models and adds acknowledge_login to update last_login on demand.
  • Backend: adds onboarding_url config and conditionally attaches onboarding_redirect_url to GET /users/current.
  • Frontend: on auth check, acknowledges login and redirects first-time users to onboarding when configured.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
dwctl/src/config.rs Adds onboarding_url: Option<String> to config with default None.
config.yaml Documents the optional onboarding_url setting.
dwctl/src/db/models/users.rs Adds last_login to DB response and acknowledge_login to update request.
dwctl/src/db/handlers/users.rs Maps last_login through conversion and updates SQL to set last_login when acknowledged.
.sqlx/query-298a6337....json Updates SQLx offline metadata for the modified user update query.
dwctl/src/db/handlers/organizations.rs Sets last_login: None for virtual org-user constructions.
dwctl/src/api/models/users.rs Exposes onboarding_redirect_url and fixes last_login mapping; adds acknowledge_login to UserUpdate.
dwctl/src/api/handlers/users.rs Adds onboarding redirect URL to /users/current responses when appropriate.
dwctl/src/api/handlers/auth.rs Threads acknowledge_login: None through existing update calls.
dwctl/src/api/handlers/payments.rs Threads acknowledge_login: None through existing update calls.
dwctl/src/auth/current_user.rs Updates test fixture usage to include acknowledge_login.
dwctl/src/test/utils.rs Updates test config and user fixtures with new fields.
dwctl/src/db/handlers/api_keys.rs Updates test config fixture to include onboarding_url.
dashboard/src/api/control-layer/types.ts Adds last_login, onboarding_redirect_url, and acknowledge_login to TS types.
dashboard/src/contexts/auth/context.tsx Adds login acknowledgement + first-login redirect logic in checkAuthStatus.

@sejori
Copy link
Contributor Author

sejori commented Mar 11, 2026

@JoshC8C7 I've been looking into ways to get a claim from auth0 and parse it in the iso-stack but there doesn't seem to be a smooth way to do it. I also realised that the current approach would only update for dashboard users and not API KEY use in the OpenAI SDK. So instead I will go for a much simpler solution that just updates the last_login column via the auth middleware on every request where the current value of last_login is more than one hour ago.

@sejori sejori enabled auto-merge March 11, 2026 14:18
@sejori sejori added this pull request to the merge queue Mar 11, 2026
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Mar 11, 2026
@sejori sejori added this pull request to the merge queue Mar 11, 2026
Merged via the queue into main with commit 45f5138 Mar 11, 2026
12 of 13 checks passed
github-merge-queue bot pushed a commit that referenced this pull request Mar 11, 2026
🤖 I have created a release *beep* *boop*
---


##
[8.18.0](v8.17.1...v8.18.0)
(2026-03-11)


### Features

* allow email reuse in orgs with updated unique indx on user emails
([#853](#853))
([f4ca358](f4ca358))
* migration to allow org members to have the same api key name
([#857](#857))
([09b6001](09b6001))
* use last_login column to detect first login and redirect onboarding
([#835](#835))
([45f5138](45f5138))


### Bug Fixes

* make get_pending_request_counts count processing and claimed too
([#836](#836))
([ae6fbda](ae6fbda))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants