Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invalidated immediately after authentication when using cookieStore with Safari #2533

Closed
joshuaswift opened this issue Mar 28, 2023 · 8 comments

Comments

@joshuaswift
Copy link

Safari version: 14.1.2
ESA version: 4.2.2
Ember version: 4.9.3

We recently switched from using the adaptiveStore to the cookieStore so we can keep the user authenticated across various subdomains.

This works perfectly on Chrome and Firefox, but on Safari we're experiencing an issue where immediately after signing in using the authenticate method, the session becomes invalidated and the user is redirected straight back to the login screen.

I've dug through the source code and it seems to be caused by the _bindToStoreEvents method in the internal-session service.

This gets called in the init hook for this service, and when it gets to the conditional which checks for an authenticatorFactory, it's undefined which causes the _clear method to be called which then invalidates the session.

The strange thing is, in Chrome this _bindToStoreEvents method never seems to get reached on login, so I'm not sure what this authenticatorFactory value should be, or why it's undefined in this scenario.

If I then delete the cookies generated by the initial login, I can login fine and it doesn't invalidate me, but every subsequent time I try and login it will repeat the original issue unless I delete the cookies each time.

I have also checked that all cookies are allowed in Safari by disabling 'Block all cookies' and 'Prevent cross-site tracking' just in case this was interfering with ESA somehow.

Any ideas of how to resolve this?

@BobrImperator
Copy link
Collaborator

Hi @joshuaswift, thanks for reporting the issue and all the pointers.

At this very moment, I can't tell you what is the issue, I'll try to work that out in the coming days though.

authenticatorFactory is a string used to look up a service/object in the Ember's DI system.
Considering this example from the ESA's readme https://github.com/mainmatter/ember-simple-auth#walkthrough

  @action
  async authenticate(e) {
    e.preventDefault();
    let { identification, password } = this;
    try {
      await this.session.authenticate('authenticator:oauth2', identification, password);
    } catch(error) {
      this.errorMessage = error.error || error;
    }

    if (this.session.isAuthenticated) {
      // What to do with all this success?
    }
  }

authenticate is the method you need to use when logging-in to the app, as the first argument it expects the authenticatorFactory string. In this case it's "authenticator:oauth2".
It then is assigned to a property of the internal-session over here in this._setup
and then presisted in a store over here.
So later it can be used for restoring the session.

@BobrImperator
Copy link
Collaborator

Unfortunately I couldn't reproduce this.
I'm also attaching a screen recording.

ember-simple-auth 4.2.2
ember-simple-auth-token 5.2.2
Safari 15.1

The value of the cookie also contains the authenticator and it's exactly the same string as the one provided during authenticate call.

Could you make sure that the cookie is correct after/during log-in? e.g.

{
    "authenticator": "authenticator:token",
    "access_token": "....",
    "status": "ok"
}

This gets called in the init hook for this service, and when it gets to the conditional which checks for an authenticatorFactory, it's undefined which causes the _clear method to be called which then invalidates the session.

The strange thing is, in Chrome this _bindToStoreEvents method never seems to get reached on login, so I'm not sure what this authenticatorFactory value should be, or why it's undefined in this scenario.

Additonally, in my testing neither on Chrome or Safari the sessionDataUpdated callback isn't run during authenticate (as it shouldn't).
It's triggered by the authenticator, however while authenticate request is still not resolved, the event doesn't do anything. So my only guess at the moment is that your authenticator triggers the sessionDataUpdated event after authentication.

What authenticator do you use?

Code samples and recording:

// app/session-stores/application.js
import CookieStore from 'ember-simple-auth/session-stores/cookie';

const CustomCookieStore = CookieStore.extend({});

export default CustomCookieStore;
  @action
  async authenticate() {
    const authenticator = 'authenticator:token'; // or 'authenticator:jwt'

    try {
      await this.session.authenticate(
        authenticator,
        {
          user: {
            name: this.form.username,
            password: this.form.password,
          },
        }
      );

      this.router.transitionTo('protected.catalysts');
    } catch (error) {
      // dont matter
    }
  }
Screen.Recording.2023-04-05.at.20.48.22.mov

@joshuaswift
Copy link
Author

Thanks for the response! Interesting, we use the devise authenticator but extend it with some basic config, see below:

User authenticator:

// app/authenticators/user.js
import {alias} from '@ember/object/computed';
import {computed} from '@ember/object';
import {inject as service} from '@ember/service';
import classic from 'ember-classic-decorator';
import DeviseAuthenticator from 'ember-simple-auth/authenticators/devise';

@classic
export default class User extends DeviseAuthenticator {
  @service global;

  @alias('global.apiHost') host;

  @computed('host')
  get serverTokenEndpoint() {
    return this.host + '/users/sign_in';
  }
}

Session store:

// app/session-stores/application.js
import CookieStore from 'ember-simple-auth/session-stores/cookie';

const CustomCookieStore = CookieStore.extend({});

export default CustomCookieStore;

Login logic:

 @action
  async authenticate() {
    this.isLoading = true;

    try {
      await this.session.authenticate('authenticator:user', this.email, this.password);

      this.router.transitionTo('routeAfterAuthentication');
    } catch (error) {
      if (error?.graphQLErrors?.[0]?.symbol === 'needs_password_change')
        return this.router.transitionTo('login.force-password-change');

      const errorMessage = await error?.json?.();

      this.invalidMessage = this._parseErrorMessage(errorMessage);
    }

    this.isLoading = false;
  }

Apologies I can't provide a screen recording, but I checked the session after authenticating and the cookie is present as expected, but it then gets invalidated immediately when the sessionDataUpdated event is triggered.

@BobrImperator
Copy link
Collaborator

BobrImperator commented Apr 7, 2023

@joshuaswift I tried to reproduce it again based on your setup (devise authenticator and RoR server) and again it appears to be working correctly for me.
At this point I don't have any other ideas than trying to update Safari and see if the problem persists.

I'd try also:

  • Open Safari without any extensions
  • See if there's any code that could be modifying cookies or the ESA's session data
  • Update Safari

@spuxx1701
Copy link

spuxx1701 commented Apr 10, 2023

I'm observing similar behavior on both Chrome and Firefox, also using CookieStore. Immediately after a successful login, the session is invalidated. I don't think it's ESA's fault though, since even rolling back to 4.2.2 didn't solve the issue for me. I've been merging dependabot PRs a bit carelessly and I'm suspecting that it might be caused by something else. 🤔
For now, I've rolled back my changes and now I'll wait for the next batch of dependabot alerts. I'll try and see whether I can find out what caused the issue for me.

@spuxx1701
Copy link

I've updatd to 5.0.0 again and merged all my dependabot PRs carefully one at a time, and I couldn't reproduce the issue. I'm afraid I have no idea what caused it. 😵‍💫

@BobrImperator
Copy link
Collaborator

Seems like good but bizarre news 👯 😄

I'll be closing this based on spuxx's comment. @joshuaswift feel free to reach out and re-open if your issue still persists.

@spuxx1701
Copy link

spuxx1701 commented May 18, 2023

@BobrImperator @joshuaswift I managed to reproduce the behavior. I was able to isolate it to ember-data: 4.12.0. It happens on both 5.0.0 and 4.2.2 of ember-simple-auth, so it's likely an issue with ember-data. Before moving there I wanted to let you know and ask whether you might have an idea what might be going on here.

Both of these lead to the observed behavior of sessions being invalidated right after login:

"ember-data": "~4.12.0",
"ember-simple-auth": "^4.2.2",
"ember-data": "~4.12.0",
"ember-simple-auth": "^5.0.0",

Both of these work fine:

"ember-data": "~4.11.3",
"ember-simple-auth": "^4.2.2",
"ember-data": "~4.11.3",
"ember-simple-auth": "^5.0.0",

This will likely get reported more often once folk update their apps, so we should probably get on top of this. I can try to dig into what's going on, but it might take me some time as I'm currently on vacation. Everything points to ember-data right now, but no idea why. Guess we'll need to take a look at what changed on 4.12.0.

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

No branches or pull requests

3 participants