Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
23 changes: 23 additions & 0 deletions e2e/tests/admin/signin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,27 @@ test.describe('Ghost Admin - Signin Redirect', () => {

await postsPage.waitForPageToFullyLoad();
});

test('query params on a deep link survive signin redirect', async ({page, ghostAccountOwner}) => {
// Newsletter reply-to verification emails point at
// /settings/newsletters/?verifyEmail=<token>. If the user clicks the
// link in a browser that isn't signed in, the param needs to survive
// the signin round-trip, otherwise the verify-on-mount handler in
// newsletters.tsx no-ops and the customer thinks their reply-to
// address didn't save (ONC-1618 / ONC-1642).
await logout(page);

await page.goto('/ghost/#/settings/newsletters/?verifyEmail=fake-token-xyz');

const loginPage = new LoginPage(page);
await expect(loginPage.signInButton).toBeVisible();

await loginPage.signIn(ghostAccountOwner.email, ghostAccountOwner.password);

// The error modal is the signal that the React verify handler ran:
// it only renders when newsletters.tsx attempted to redeem a token
// and the API rejected it (expected, since the token is fake).
await expect(page.getByRole('heading', {name: 'Error verifying email address'})).toBeVisible();
expect(page.url()).toContain('verifyEmail=fake-token-xyz');
});
});
8 changes: 7 additions & 1 deletion ghost/admin/app/services/session.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import AuthConfiguration from 'ember-simple-auth/configuration';
import ESASessionService from 'ember-simple-auth/services/session';
import RSVP from 'rsvp';
import windowProxy from 'ghost-admin/utils/window-proxy';
import {configureScope} from '@sentry/ember';
import {getOwner} from '@ember/application';
import {inject} from 'ghost-admin/decorators/inject';
Expand Down Expand Up @@ -91,7 +93,11 @@ export default class SessionService extends ESASessionService {
const redirectUrl = window.sessionStorage.getItem('ghost-signin-redirect');
window.sessionStorage.removeItem('ghost-signin-redirect');
if (redirectUrl && !redirectUrl.startsWith('/signin') && !redirectUrl.startsWith('/signup') && !redirectUrl.startsWith('/setup')) {
this.router.transitionTo(redirectUrl);
// Hard navigate rather than router.transitionTo: the catch-all
// react-fallback route has no controller-declared queryParams,
// so transitionTo strips params like ?verifyEmail=<token> used
// by newsletter reply-to confirmation links.
windowProxy.replaceLocation(`${AuthConfiguration.rootURL}#${redirectUrl}`);
return;
}

Expand Down
Loading