diff --git a/ghost/admin/app/components/modal-re-authenticate.js b/ghost/admin/app/components/modal-re-authenticate.js index 65f11df14e..f7f3acd36f 100644 --- a/ghost/admin/app/components/modal-re-authenticate.js +++ b/ghost/admin/app/components/modal-re-authenticate.js @@ -1,4 +1,3 @@ -import $ from 'jquery'; import ModalComponent from 'ghost-admin/components/modal-base'; import ValidationEngine from 'ghost-admin/mixins/validation-engine'; import {htmlSafe} from '@ember/template'; @@ -43,7 +42,8 @@ export default ModalComponent.extend(ValidationEngine, { _passwordConfirm() { // Manually trigger events for input fields, ensuring legacy compatibility with // browsers and password managers that don't send proper events on autofill - $('#login').find('input').trigger('change'); + const inputs = document.querySelectorAll('#login input'); + inputs.forEach(input => input.dispatchEvent(new Event('change'))); this.set('authenticationError', null); diff --git a/ghost/admin/app/controllers/editor.js b/ghost/admin/app/controllers/editor.js index 49b45507dd..ed1580cea8 100644 --- a/ghost/admin/app/controllers/editor.js +++ b/ghost/admin/app/controllers/editor.js @@ -284,6 +284,15 @@ export default Controller.extend({ }, toggleReAuthenticateModal() { + if (this.showReAuthenticateModal) { + // closing, re-attempt save if needed + if (this._reauthSave) { + this.saveTask.perform(this._reauthSaveOptions); + } + + this._reauthSave = false; + this._reauthSaveOptions = null; + } this.toggleProperty('showReAuthenticateModal'); }, @@ -490,6 +499,12 @@ export default Controller.extend({ return post; } catch (error) { + if (this.showReAuthenticateModal) { + this._reauthSave = true; + this._reauthSaveOptions = options; + return; + } + this.set('post.status', prevStatus); if (error === undefined && this.post.errors.length === 0) { diff --git a/ghost/admin/app/services/ajax.js b/ghost/admin/app/services/ajax.js index f7b8452042..60c0f71627 100644 --- a/ghost/admin/app/services/ajax.js +++ b/ghost/admin/app/services/ajax.js @@ -1,7 +1,7 @@ import AjaxService from 'ember-ajax/services/ajax'; import config from 'ghost-admin/config/environment'; import moment from 'moment'; -import {AjaxError, isAjaxError} from 'ember-ajax/errors'; +import {AjaxError, isAjaxError, isForbiddenError} from 'ember-ajax/errors'; import {captureMessage} from '@sentry/browser'; import {get} from '@ember/object'; import {isArray as isEmberArray} from '@ember/array'; @@ -279,15 +279,19 @@ let ajaxService = AjaxService.extend({ let isGhostRequest = GHOST_REQUEST.test(request.url); let isAuthenticated = this.get('session.isAuthenticated'); let isUnauthorized = this.isUnauthorizedError(status, headers, payload); + let isForbidden = isForbiddenError(status, headers, payload); // used when reporting connection errors, helps distinguish CDN if (isGhostRequest) { this._responseServer = headers.server; } - if (isAuthenticated && isGhostRequest && isUnauthorized) { + if (isAuthenticated && isGhostRequest && (isUnauthorized || (isForbidden && payload.errors?.[0].message === 'Authorization failed'))) { this.skipSessionDeletion = true; this.session.invalidate(); + // skip showing alert message. Wouldn't be shown if fully logged out, + // is unneeded when showing re-authenticate modal + return; } return this._super(...arguments); diff --git a/ghost/admin/app/services/session.js b/ghost/admin/app/services/session.js index eb9b5506d2..86248ba037 100644 --- a/ghost/admin/app/services/session.js +++ b/ghost/admin/app/services/session.js @@ -56,6 +56,11 @@ export default class SessionService extends ESASessionService { } async handleAuthentication() { + if (this.skipAuthSuccessHandler) { + this.skipAuthSuccessHandler = false; + return; + } + try { await this.populateUser(); } catch (err) { @@ -64,11 +69,6 @@ export default class SessionService extends ESASessionService { await this.postAuthPreparation(); - if (this.skipAuthSuccessHandler) { - this.skipAuthSuccessHandler = false; - return; - } - super.handleAuthentication('home'); }