feat: use last_login column to detect first login and redirect onboarding#835
feat: use last_login column to detect first login and redirect onboarding#835
Conversation
…o configured onboarding_redirect_url
Deploying control-layer with
|
| 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 |
|
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) |
|
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 |
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. |
There was a problem hiding this comment.
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_loginthrough DB/API models and addsacknowledge_loginto updatelast_loginon demand. - Backend: adds
onboarding_urlconfig and conditionally attachesonboarding_redirect_urltoGET /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. |
|
@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 |
🤖 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).
Changes:
Backend (Rust)
dwctl/src/config.rs— Addedonboarding_url: Option<String>toConfigstruct, defaulting toNone. When set, new users are redirected to this URL after their first login.config.yaml— Added commented-outonboarding_urlexample for local dev.dwctl/src/db/models/users.rs— Addedlast_logintoUserDBResponse(was being dropped during conversion). Addedacknowledge_login: Option<bool>toUserUpdateDBRequestso the frontend can trigger settinglast_login.dwctl/src/db/handlers/users.rs— Plumbedlast_loginthrough theFromconversion. Added SQLCASEto the UPDATE query: whenacknowledge_loginis true, setslast_login = NOW().dwctl/src/db/handlers/organizations.rs— Addedlast_login: Noneto virtual org user constructions.dwctl/src/api/models/users.rs— Addedonboarding_redirect_url: Option<String>toUserResponse(skipped if None). Addedacknowledge_logintoUserUpdate. Addedwith_onboarding_redirect_url()builder method. Fixedlast_loginmapping (was hardcoded toNone).dwctl/src/api/handlers/users.rs— ForGET /users/current: iflast_loginis null andonboarding_urlis configured, attachesonboarding_redirect_urlto the response.Test fixtures — Added new fields (
onboarding_url,acknowledge_login,onboarding_redirect_url) acrosstest/utils.rs,api_keys.rs,auth.rs,payments.rs,current_user.rs.Frontend (TypeScript/React)
dashboard/src/api/control-layer/types.ts— Addedlast_login,onboarding_redirect_urltoUserinterface. Addedacknowledge_logintoUserUpdateRequest.dashboard/src/contexts/auth/context.tsx— IncheckAuthStatus, after fetching the current user:sessionStorageto avoid redundant PATCHes within a sessionacknowledge_login: trueto setlast_login(prevents future redirects)?redirect=URL param (org invite links take priority)Deployment
Need to ensure
onboarding_url: "https://onboarding.doubleword.ai"for production deployments.Flow