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

Add initial event to onAuthStateChange #147

Closed
wants to merge 2 commits into from

Conversation

lsrugo
Copy link

@lsrugo lsrugo commented Oct 16, 2021

What kind of change does this PR introduce?

Fixes #326

What is the current behavior?

There is no event fired unless a sign in occurs, but that is sometimes delayed. So there is no way to know if the user is signed in or not right when the page loads

What is the new behavior?

There is an INITIAL event fired after the library is finished loading and checking for a logged in user.

@kiwicopple
Copy link
Member

looks like the failing tests are from gotrue - not your changes. Let me investigate

@lsrugo
Copy link
Author

lsrugo commented Oct 18, 2021

The format of the session object changed on the backend. It returns an array for session.provider instead of a string. Should we update the tests to match that?

Copy link

@gustavohenke gustavohenke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like consumers can't know if the user is really logged out or it's just a delayed auth initialisation if they attach onAuthStateChanged after the initial event has been sent.

Should every new listener be notified of the current auth state?

Not sure if people around here like comparisons, but I immediately recognised this API coming from Firebase, and that's how it works over there 😅

@gustavohenke
Copy link

Adding a bit more food for thought on the above:
Calling .session() or .user() will return null if the session is expired, though it can be refreshed. So checking these when attaching the auth state listener won't work.

@lsrugo
Copy link
Author

lsrugo commented Oct 20, 2021

Adding a bit more food for thought on the above:
Calling .session() or .user() will return null if the session is expired, though it can be refreshed. So checking these when attaching the auth state listener won't work.

Maybe inside .session() or .user() we should also check if the loading is finished and wait for that before returning null

@mryechkin
Copy link

Has this change been given more consideration? Would love to see it implemented

@@ -86,17 +86,27 @@ export default class GoTrueClient {
headers: settings.headers,
cookieOptions: settings.cookieOptions,
})

// an array of promises which load the current user
let initPromises: Array<Promise<any>> = []
Copy link

@prescience-data prescience-data Jan 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could all this be simplified to

Promise.all([
  this._recoverSession(),
  this._recoverAndRefresh(),
  this.getSessionFromUrl({ storeSession: true })
])
  .then(([, , { error }]) => {
    if (error) {
      console.error("Error getting session from URL.", error)
    }
  })
  .catch((error) => {
    console.error("Error recovering session.", error)
    // Any other error handling...
  })
  .finally(() =>
    this._notifyAllSubscribers("INITIALIZED") // Other events appear to be past-tense.
  )

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@prescience-data
Copy link

prescience-data commented Jan 8, 2022

#147 (comment) @gustavohenke
Could this be solved potentially but setting an isInitialized boolean on the client which can be checked at the same time as attaching listeners?
Still not sure if this would be 100% atomic though (ie vs passing in a callback to the constructor).

Alternatively the client could present async isInitialized() as an async function which returns immediately if the state is already known, or awaits the finally {} in the constructor's Promise.all()? This would enable the second approach @lsrugo mentioned here: #147 (comment)

// ...
class GoTrueClient {
  private _isInitialized: boolean = false

  public constructor() {
    // ....
    Promise.all([
      this._recoverSession(),
      this._recoverAndRefresh(),
      this.getSessionFromUrl({ storeSession: true })
    ])
      .then(([, , { error }]) => {
        if (error) {
          console.error("Error getting session from URL.", error)
        }
      })
      .catch((error) => {
        console.error("Error recovering session.", error)
        // Any other error handling...
      })
      .finally(() => {
        this._notifyAllSubscribers("INITIALIZED") // Other events appear to be past-tense.
        this._isInitialized = true
      })
  }

  public async isInitialized(): Promise<boolean> {
    if (this._isInitialized) {
      return true
    }
    return new Promise((resolve) => {
      this.onAuthStateChange((event) => {
        if (event === "INITIALIZED") {
          return resolve(true)
        }
      })
    })
  }
}

@hf
Copy link
Contributor

hf commented Feb 2, 2023

Thank you for this contribution, however the library has advanced quite a bit.

@hf hf closed this Feb 2, 2023
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

Successfully merging this pull request may close these issues.

onAuthStateChange should fire on initial load
6 participants