fix(auth): preserve organization invitations through login - #2528
fix(auth): preserve organization invitations through login#2528niemyjski wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3baf39f1f9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| onSubmit: LoginSchema, | ||
| onSubmitAsync: async ({ value }) => { | ||
| const response = await login(value.email, value.password); | ||
| const response = await login(value.email, value.password, value.invite_token); |
There was a problem hiding this comment.
Select the invited organization after login
When an existing user has another valid organization persisted in organization.current, this call consumes the invitation but the success path still navigates to the default /; the app layout preserves any currently valid selection, so the invited organization is not selected. The same problem affects invitation-based OAuth signup, which redirects to the add-project page and can consequently default a new project to the user's previous organization. Return or resolve the invited organization and select it before continuing.
AGENTS.md reference: AGENTS.md:L72-L75
Useful? React with 👍 / 👎.
| import { toast } from 'svelte-sonner'; | ||
|
|
||
| const organizationId = organization.current!; | ||
| const organizationId = page.params.organizationId || organization.current!; |
There was a problem hiding this comment.
Make the route organization ID reactive
When client-side navigation changes only [organizationId] while this users page remains mounted, this plain const retains the old parameter. The parent layout and heading switch to the new organization, but the users query, invite mutation, and table options continue targeting the previous organization, so an administrator can view or modify the wrong membership list. Derive the ID reactively from page.params instead.
AGENTS.md reference: src/Exceptionless.Web/ClientApp/AGENTS.md:L21-L23
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 14799e817d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const response = await client.postJSON<TokenResult>(`auth/${options.provider}`, { | ||
| clientId: options.clientId, | ||
| code: data.code, | ||
| inviteToken: options.inviteToken, |
There was a problem hiding this comment.
Keep invitation OAuth separate from account linking
When a user opens an invitation while already signed in, the shared fetch client sends the existing access token with this OAuth request. AuthHandler.FromExternalLoginAsync therefore enters its authenticated account-linking branch (lines 566-585) instead of logging in the OAuth user; if the selected OAuth identity belongs to another Exceptionless account, it is removed from that account and attached to the current one, after which the forwarded invitation is also granted to the current account. This can lock the intended account out and consume its invitation, so invitation login must omit the current authentication or otherwise distinguish login from account linking.
Useful? React with 👍 / 👎.
Deep-linking an organization users URL while a different organization was previously selected captured the stale persisted id at component init, querying the wrong organization's users under the current route. Derive the id from the route param and thread it through table options as a getter so columns rebuild with the live value.
Billing and invoice queries read the persisted selected-organization store instead of the route, so cold deep-links fetched the wrong organization's billing data before the layout effect synced the store.
…valid invites Renames the new e2e test to match the sentence-case suite convention and adds backend coverage asserting an invalid invite token is forbidden when account creation is disabled and no user is created.
14799e8 to
18072df
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 18072df35a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| const defaultRedirect = resolve('/'); | ||
| const redirectUrl = getSafeRedirectUrl(page.url.searchParams.get('redirect'), defaultRedirect); | ||
| const inviteToken = page.url.searchParams.get('token'); |
There was a problem hiding this comment.
Keep the invitation token reactive
When client-side navigation or a popstate transition changes only the login route's token query parameter, this plain constant retains the previous token. The signup links and form therefore continue submitting that stale token, which can consume the wrong organization invitation; bind the query parameter through the shared reactive query-parameter utility and keep the form value synchronized.
AGENTS.md reference: src/Exceptionless.Web/ClientApp/AGENTS.md:L27-L27
Useful? React with 👍 / 👎.
Summary
The invitation email already links to /signup?token=... on main. This PR fixes the independent acceptance gap discovered while auditing the email modernization work; it contains no email-rendering changes.
Verification
Post-Deploy Monitoring & Validation
Verify both an existing user and a new OAuth user can accept an organization invitation from the tokenized signup link, including when public account creation is disabled. Confirm the invite is consumed and the invited organization is selected after authentication.