Giter Site home page Giter Site logo

supabase / auth-js Goto Github PK

View Code? Open in Web Editor NEW
296.0 19.0 138.0 4.9 MB

An isomorphic Javascript library for Supabase Auth.

License: MIT License

JavaScript 0.92% TypeScript 8.40% PLpgSQL 0.13% HTML 0.07% CSS 90.48%
authentication go supabase sponsors authorization jwt user-management javascript typescript

auth-js's Introduction

Supabase

Supabase is an open source Firebase alternative. We're building the features of Firebase using enterprise-grade open source tools.

  • Hosted Postgres Database. Docs
  • Authentication and Authorization. Docs
  • Auto-generated APIs.
  • Functions.
    • Database Functions. Docs
    • Edge Functions Docs
  • File Storage. Docs
  • AI + Vector/Embeddings Toolkit. Docs
  • Dashboard

Supabase Dashboard

Watch "releases" of this repo to get notified of major updates.

Watch this repo

Documentation

For full documentation, visit supabase.com/docs

To see how to Contribute, visit Getting Started

Community & Support

  • Community Forum. Best for: help with building, discussion about database best practices.
  • GitHub Issues. Best for: bugs and errors you encounter using Supabase.
  • Email Support. Best for: problems with your database or infrastructure.
  • Discord. Best for: sharing your applications and hanging out with the community.

How it works

Supabase is a combination of open source tools. We’re building the features of Firebase using enterprise-grade, open source products. If the tools and communities exist, with an MIT, Apache 2, or equivalent open license, we will use and support that tool. If the tool doesn't exist, we build and open source it ourselves. Supabase is not a 1-to-1 mapping of Firebase. Our aim is to give developers a Firebase-like developer experience using open source tools.

Architecture

Supabase is a hosted platform. You can sign up and start using Supabase without installing anything. You can also self-host and develop locally.

Architecture

  • Postgres is an object-relational database system with over 30 years of active development that has earned it a strong reputation for reliability, feature robustness, and performance.
  • Realtime is an Elixir server that allows you to listen to PostgreSQL inserts, updates, and deletes using websockets. Realtime polls Postgres' built-in replication functionality for database changes, converts changes to JSON, then broadcasts the JSON over websockets to authorized clients.
  • PostgREST is a web server that turns your PostgreSQL database directly into a RESTful API
  • GoTrue is a JWT based API for managing users and issuing JWT tokens.
  • Storage provides a RESTful interface for managing Files stored in S3, using Postgres to manage permissions.
  • pg_graphql a PostgreSQL extension that exposes a GraphQL API
  • postgres-meta is a RESTful API for managing your Postgres, allowing you to fetch tables, add roles, and run queries, etc.
  • Kong is a cloud-native API gateway.

Client libraries

Our approach for client libraries is modular. Each sub-library is a standalone implementation for a single external system. This is one of the ways we support existing tools.

Language Client Feature-Clients (bundled in Supabase client)
Supabase PostgREST GoTrue Realtime Storage Functions
⚡️ Official ⚡️
JavaScript (TypeScript) supabase-js postgrest-js gotrue-js realtime-js storage-js functions-js
Flutter supabase-flutter postgrest-dart gotrue-dart realtime-dart storage-dart functions-dart
Swift supabase-swift postgrest-swift auth-swift realtime-swift storage-swift functions-swift
💚 Community 💚
C# supabase-csharp postgrest-csharp gotrue-csharp realtime-csharp storage-csharp functions-csharp
Go - postgrest-go gotrue-go - storage-go functions-go
Java - - gotrue-java - storage-java -
Kotlin supabase-kt postgrest-kt gotrue-kt realtime-kt storage-kt functions-kt
Python supabase-py postgrest-py gotrue-py realtime-py storage-py functions-py
Ruby supabase-rb postgrest-rb - - - -
Rust - postgrest-rs - - - -
Godot Engine (GDScript) supabase-gdscript postgrest-gdscript gotrue-gdscript realtime-gdscript storage-gdscript functions-gdscript

Badges

Made with Supabase

[![Made with Supabase](https://supabase.com/badge-made-with-supabase.svg)](https://supabase.com)
<a href="https://supabase.com">
  <img
    width="168"
    height="30"
    src="https://supabase.com/badge-made-with-supabase.svg"
    alt="Made with Supabase"
  />
</a>

Made with Supabase (dark)

[![Made with Supabase](https://supabase.com/badge-made-with-supabase-dark.svg)](https://supabase.com)
<a href="https://supabase.com">
  <img
    width="168"
    height="30"
    src="https://supabase.com/badge-made-with-supabase-dark.svg"
    alt="Made with Supabase"
  />
</a>

Translations

auth-js's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

auth-js's Issues

Signup error type Error does not include a "status" property

Bug report

Describe the bug

// when calling...
const { user, session, error } = await supabase.auth.signUp({
	email: userInput.email,
	password: userInput.password
});

// and error is:
error: {
    message: 'A user with this email address has already been registered',
    status: 400
}

error type is JS Error, which does not include the status property, so trying to access error.status results in ESLint warning Property 'status' does not exist on type 'Error'.ts(2339)

To Reproduce

I do not have working hosted project yet.

Expected behavior

error type (AuthError?) should include status property.

Screenshots

Screen Shot 2021-06-03 at 1 57 39 PM

add ".local" to checks in `isSecureEnvironment`

Feature request

I would like to add ".local" to the list of hosts checked in src/lib/cookies.js

Is your feature request related to a problem? Please describe.

The problem I am facing is when developing a multi-tenanted application in my local environment, I define the tenant in my hosts file as tenant-name.application.local.
The isSecureEnvironment method in src/lib/cookies.js checks the host header to include 127.0.0.1 and localhost which returns false, causing the cookie to be set as Secure:

Describe the solution you'd like

I would like to add ".local" to the list of hosts checked in src/lib/cookies.js

Describe alternatives you've considered

Changing my hosts file to use tenant-name.application.localhost, however when accessing the application from other machines on the network it might be confusing, calling a remote host that ends with .localhost.

I believe the .local tld is fairly commonly used to resolve a machine on your local network.

Additional context

Adding ".local" to the array might return some false positives due to the way the values are checked.
EG: [".local", ...].includes(host) would return true for the fictional vhost: app.localbusiness.com.

Maybe it would be better suited to change the check from:

  if (['localhost', '127.0.0.1'].indexOf(host) > -1) {
    return false
  }

To:

  if (['localhost', '127.0.0.1'].indexOf(host) > -1 || host.endsWith('.local')) {
    return false
  }

Would be better suited.

RFC: Split client-side SDK from admin (service role) SDK

Describe the problem

Currently, the admin methods like "Delete user" that require a full-access API key (e.g. service_role key) are documented side-by-side with the more client facing methods like signUp or signOut (that I think only require a public API key).

I will likely only use the admin methods in a server-side environment (because I should never send a service_role key to a client). I will likely not use any of the client-side methods (e.g. signUp, signIn, signOut) in that server-side environment. For the same reasons, I will never use the admin methods in a client-side environment. Thus, it doesn't make any sense to increase the client-side bundle by including methods that will only ever be called on the server-side (and vice versa).

Describe the improvement

I think this library should be refactored into two separate JavaScript libraries:

  1. A Node.js admin SDK - For server-side environments, only includes admin methods that require a service_role key.
  2. A JavaScript client-side SDK - To be bundled with client-side code, only includes methods called client-side.

And then the documentation should be updated accordingly: split (much like the Firebase Authentication docs and reference) into two sections, one for the admin SDK and one for the client-side SDK.

Additional context

This is how the Firebase Authentication SDKs are structured and I think it makes a lot of sense to reproduce a similar structuring here with Supabase Auth. Especially because I think that every REST API method included in the GoTrue /admin endpoints should be reproduced in a server-side admin SDK (e.g. like how the DELETE /admin/users/{user_id} was wrapped in #85).

For example, I'd like to be able to programmatically create new users without them having to login client-side.

Bug: signOut function is not completing and thus not creating logout events in log table

https://github.com/supabase/gotrue-js/blob/4daa22cf30a2fe25bd1df9d9df0061c17d81a691/src/GoTrueClient.ts#L314

The signOut function never calls this.api.signOut because it's first removing the current session with this._removeSession(), then later checks for this.currentSession before calling this.api.signOut, so this.api.signOut never gets called.

Essentially, no logout is ever taking place. The code is only removing the current session.

The result of this is that if you look in the log table you only see login events, even if you log in, log out, log in, log out, etc.

async signOut(): Promise<{ error: Error | null }> {
    this._removeSession()
    this._notifyAllSubscribers('SIGNED_OUT')
    if (this.currentSession) {
      const { error } = await this.api.signOut(this.currentSession.access_token)
      if (error) return { error }
    }
    return { error: null }
  }

autoRefreshToken doesn't work

Bug report

Describe the bug

After initialising supabase client with

createClient(supabaseUrl, supabaseKey, {
  localStorage: AsyncStorage as any,
  autoRefreshToken: true
});

and setting the JWT expiry to 60 (seconds) on the supabase dashboard, the token does not refresh after 60 seconds have elapsed. Oddly enough, setting the JWT expiry to anything less than 60 triggers the token refresh but it constantly updates without waiting for expiry

To Reproduce

Steps to reproduce the behavior, please provide code snippets or a repository:

  1. Setup RN project (I used expo bare)
  2. Set JWT expiry to 60
  3. Add supabase auth state listener
  4. Sign in
  5. Wait for refresh token to update (doesn't update)

Expected behavior

The token should refresh after the expiry time set on the supabase dashboard

System information

  • OS: macOS
  • Version of supabase-js: 1.11.14
  • Version of Node.js: 16.1.0

Enable third party auth from access token and/or code

Feature request

Ability to use access token or other credential received from OAuth flow to enable third party auth.

Is your feature request related to a problem? Please describe.

I am using Expo for my app which takes care a lot of the nuances in handling OAuth flows in a React Native / Expo managed app: https://docs.expo.io/guides/authentication/. Right now, trying to use the built-in provider flows from Supabase JS client is not working.

Describe the solution you'd like

I would like a way to send an access token or other credential received from an OAuth flow to Supabase to facilitate the login.

Describe alternatives you've considered

Not offering third party auth.

[Feature Request] Emit a "ready state" event from onAuthStateChange

Feature request

Is your feature request related to a problem?

When bootstrapping a frontend application, it is useful to block the final render until the application knows if it is authenticated or not.

The base GoTrueClient instance provides no real method to infer a post-recovery attempt ready state (only emits upon SIGNED_IN success event).

Describe the solution you'd like

Would be awesome to augment the base GoTrueClient to emit an event if a session is not auto-recovered at construction.

References:

Triggers the auto-recovery:
https://github.com/supabase/gotrue-js/blob/4312f53411dc6fdf963f8adb48200cddf9905bd9/src/GoTrueClient.ts#L77
Only emits an event if the session is successfully recovered:
https://github.com/supabase/gotrue-js/blob/4312f53411dc6fdf963f8adb48200cddf9905bd9/src/GoTrueClient.ts#L499

If the Supabase supabase.auth instance emitted an event in both success and fail cases, then it would be possible to wrap the final render to await a decision:

No auth state check:

// Application mounts too early, meaning downstream route guards redirect to login due to an assumed unauthenticated state.
// Mount the application.
app.mount("#app")

With additional state change event to signal ready state:

// Specifics of state inspection can occur within a route guard.
// All we need to know is that the auth system has fully initialized one way or another.
supabase.auth.onAuthStateChange(() => {
  // Mount the application.
  app.mount("#app")
})

Describe alternatives you've considered

Alternative 1

Currently, I am detecting the state change (on success) within the app's central authentication state manager and redirecting if the user is already on the login page, however, this is ugly/bad UX, so a pre-render blocking solution would be preferable.

Alternative 2

A second alternative I tried previously was to race a Promise with a timeout, but that felt horribly un-atomic, which is why I landed on the prior alternative:

Promise.race([
  () =>
    new Promise<void>((resolve) => {
      setTimeout(resolve, 100)
    }),
  () =>
    new Promise<void>((resolve) => {
      supabase.auth.onAuthStateChange((event) => {
        if (event === "SIGNED_IN") {
          resolve()
        }
      })
    })
]).then(() => {
  app.mount("#app")
})

Calling `signIn()` twice returns an error

Chore

Describe the chore

Test case:

test('signUp() the same user twice should throw an error', async () => {
  let { error, data } = await api.signUpWithEmail(email, password)
  expect(error?.message).toBe('Error sending confirmation mail')
  // expect(error?.message).toBe('A user with this email address has already been registered')
  expect(data).toBeNull()
})

https://github.com/supabase/gotrue-js/blob/3501d62e4a8435a1bd749ca39cb01dc5e14efbfa/test/apiWithAutoConfirmDisabled.test.ts#L32

Additional context

It would be more helpful to show that there is a "conflict", but at the moment it shows an obscure error: Error sending confirmation mail

Related: #45 (comment)

Bug: logging out of one session removes all auth.refresh_tokens from all sessions

Bug report

Describe the bug

  • If I log into my app, I see a record in auth.refresh_tokens.
  • If I log into my app again in a new (incognito) browser window, I now see two records in auth.refresh_tokens.
  • If I log out of ONE of the browser windows, both auth.refresh_tokens records are removed, but one of the sessions is still logged in.

To Reproduce

Steps to reproduce the behavior, please provide code snippets or a repository:

  1. Log into your app from multiple locations using the same email address and password (multiple incognito browser windows works for this)
  2. Look at the auth.refresh_tokens entries table for the given user. There should be one entry per login.
  3. Now logout one of the sessions
  4. Look at auth.refresh_tokens entries again -- there should now be none.
  5. The remaining logged-on sessions still work fine

Expected behavior

I believe (and I may be wrong about this) that there should be auth.refresh_tokens records for each logged-in user instance.

System information

Tested using an Angular Ionic PWA on MacOS

  • OS: MacOS
  • Browser: Chrome
  • Version of supabase-js: 1.9.0
  • Version of Node.js: 14.16.0

User session recovery fails on react-native

Bug report

Describe the bug

I have everything wrapped in a user context provider. When there is a crash or you open the app after closing it, or even when you go back to it sometimes, it logs you out and you have to log in again. I tried a lot of things, including specifying options and double checking the code. Most of it is adapted from the Expo example. Please let me know your thoughts from the gist below:
https://gist.github.com/rodjoseph/ec6b511ea6ab0a2354b231e31121f736

Expected behavior

User session should be recovered when user comes back to the app

Better way to handle password resets.

Feature request

Is your feature request related to a problem? Please describe.

Currently, the Supabase client has an endpoint to request a magic link or a password reset. However, once the user clicks on that link and is redirected back to the application, there is no way to insert that access / refresh token back into the Supabase client. The only way to handle it is to use the GoTrue client, which feels hacky. Additionally, once they have reset their password with the GoTrue client, they will have to log in again with the Supabase client , as I believe they have two separate systems for maintaining a logged in user. (Please correct me if I am wrong here.)

Describe the solution you'd like

I would like an addition to the Supabase auth api that allows someone to log in directly with an auth token instead of a user name and password. Additionally, it would be nice to have a way to get a new auth token with a refresh token. Basically, a nice wrapper around more of the GoTrue api that integrates with all the things you have built.

Describe alternatives you've considered

I've thought about just using GoTrue directly for everything. However, I think that might cause problems with PostRest authentication, though I didn't get far enough to find out.

GoTrueClient.update() doesn't persist updated user to localstorage

Bug report

Describe the bug

After calling GoTrueClient.update(), it doesn't persist updated user to localstorage. Right now it's only update the user object in memory

Screenshot 2021-01-22 at 5 58 12 PM

To Reproduce

  1. call GoTrueClient.update() to update user
  2. refresh browser
  3. GoTrueClient.user() return old user before the update

signOut not deleting cookie

Bug report

Describe the bug

supabase.auth.signOut() is not deleting the cookie created by setAuthCookie

// /api/login.js

import { supabase } from '@/utils/initSupabase'

export default function handler(req, res) {
  supabase.auth.api.setAuthCookie(req, res)
}
// logout.js

import { supabase } from '@/utils/initSupabase'
import { useRouter } from 'next/router'
import { useEffect } from 'react'

export default function Logout() {
  const router = useRouter()

  useEffect(() => {
    async function initialize() {
      await supabase.auth.signOut()
      router.push('/login')
    }
    initialize()
  })
  return <></>
}

image

Strongly Typed Errors

Feature request

Is your feature request related to a problem? Please describe.

It is currently impossible to differentiate errors, e.g: when a user signs up there is a multitude of possible errors (network errors, database errors, etc) and although you can easily check if an error occured (if (res.error !== null) { /**/ }) it is not possible to know which error happened.

Describe the solution you'd like

Enum error types:

enum SignUpError {
  InvalidEmail,
  SignUpsDisabled,
  NetworkError,
  /* etc */
}

enum SignInError {
  InvalidCredentials,
  /* etc */
}
export default class GoTrueClient {
  // --snip--
  signUp(credentials: {
      email: string;
      password: string;
  }): Promise<{ /* success object */ } | SignUpError>;
  
  signIn(credentials: {
      email?: string;
      password?: string;
      provider?: Provider;
  }): Promise<{ /* success object */ } | SignInError>;
  // --snip--
}

Describe alternatives you've considered

I am not aware of any alternative.

Make Subscription work across browser tabs

Feature request

It would be great if Subscription (and thus supabase.auth.onAuthStateChange) would work across browser tabs; this was the behavior I actually expected (although maybe I was overly optimistic).

Usecase

The usecase I am after is to reactively synchronize authentication changes between tabs. If a user signs out on one tab, the second tab should reflect this change of authentication state.

Describe alternatives you've considered

One alternative is synchronizing authentication state by separate means somehow (e.g. using realtime subscriptions and a dedicated table); but as an app developing this is more effort than it is worth, and I would probably just go with the alternative of not bothering to synch browser tab authentication.

Prior art

swr uses revalidate on focus: https://swr.vercel.app/docs/revalidation#revalidate-on-focus

[Needs testing] AppData should only be updatable by an admin

Chore

Describe the chore

It looks like our updateUser function accepts an entire user object:
https://github.com/supabase/gotrue-js/blob/8b61cc39df22cf9e1d546df7e1d06e9b27c51556/src/GoTrueApi.ts#L162

Here is the user object:
https://github.com/supabase/gotrue-js/blob/8b61cc39df22cf9e1d546df7e1d06e9b27c51556/src/lib/types.ts#L12

The app_metadata field should ideally be updatable by the admin, and not by the user. This means it can be used for things like RBAC (netlify does it here: https://docs.netlify.com/visitor-access/role-based-access-control/)

Note, this is still an inferior system to using Postgres tables, since the user would need to log out/in to refresh their token/roles.

Additional context

  • Perhaps this is one which should be enforced on GoTrue server?

don't send expired JWTs

We might want to verify the JWT expiry date locally before sending to the server to query for data, then if there's a refresh_token stored locally we might be able to refresh it before making the request, or otherwise triggering a listenable event on the client

related: supabase/supabase#889
related: #620

Auth state change events not firing on web page load

Bug report

Describe the bug

When loading a web page with a supabase onAuthStateChange handler, one would expect the signed in event to fire once the page loads and the connection is established. But due to some recent code changes around how session restore is handled, this basically prevents any event handler from being registered. The SIGNED_IN event notification is fired as part of the Supabase client constructor.

To Reproduce

Steps to reproduce the behavior:

  1. Have an onAuthStateChanged handler.
  2. Load your script on a web page.

Expected behavior

The event handler fires.

Additional context

The problem is very apparent in a multi-page application/"regular website" with a Supabase connection. I think the issue is especially tied to this commit: 3a670be

I was using 1.7.x before, and the event would fire when the web page loads.

Maybe some kind of event handler buffer could be introduced? Buffer auth events until a handler is added, then fire them all immediately.

Type 'AsyncStorageStatic' is missing the following properties from type 'Storage': length, key

Bug report

localStorage field in options of the createClient method doesn't support type for AsyncStorage.

Describe the bug

I tried to use supabase authentication in the React Native TypeScript mobile application.
In React Native application, it uses the AsyncStorage as a storage.
When I pass the AsyncStorage as a localStorage, it triggers Type AsyncStorageStatic is missing the following properties from type Storage. error.

To Reproduce

import { createClient } from '@supabase/supabase-js'
import AsyncStorage from '@react-native-community/async-storage'

export const supabase = createClient(
  URL, API_KEY, {
    localStorage: AsyncStorage
  }
)

Expected behavior

localStorage should support type for AsyncStorage.

Screenshots

Screenshot at Mar 15 23-25-41

System information

  • OS: macOS
  • Version of @supabase/supabase-js: 1.6.0

Improve Doc comments

This is a Hacktoberfest task!
You can read more about Hacktoberfest on our blog and see all tasks on our Hacktoberfest Project Board.

Summary

We need help with checking and improving the inline comments for this library using JSDoc.

Details

If you've never see JSDoc before, it's the comment that go above functions. For example:

/**
   * Creates a new user.
   * 
   * Some longer description can go here.
   * 
   * @param {object} credentials The user login details.
   * @param {string} credentials.email The user's email address.
   * @param {string} credentials.password The user's password.
   */
  async signUp(credentials: { email: string; password: string }) {
    // ...
  }

Recover expired requests on page focus

Feature request

Is your feature request related to a problem? Please describe.

JWT tokens expire frequently.

Requiring the user to reload the page to refresh the JWT token leads to a poor user experience.

Describe the solution you'd like

  • Revalidate the JWT token on page focus
  • Revalidate the JWT token on network recovery

SWR's fetching strategy could act as an inspiration.

Additional context

The refresh token on page reload feature was implemented by @thorwebdev in #31.

Perhaps you could chime in? 😄

The token is never refreshed

Hello,

I had an issue on my app and I thought I did something wrong, but I just tried the nextjs/ssr/auth example in the supabase repo, and I also have a problem.

Repro step:

  1. Sign-up
  2. Wait 1hour
  3. Try to go on the profile page (SSR)

It will redirect you to the index page because the token is expired. And if you click on the static example, you'll have an error :

https://ibb.co/3c29dyQ

SSR -> I can't even manually use the refresh token since it's not in the cookie.
Client side -> Weird behavior, it seems that the Auth HOC never use the refresh token, and I have to logout/login.

Session not recovering in React Native

Bug report

Describe the bug

Initializing the client doesn't recover the session from AsyncStorage

To Reproduce

Steps to reproduce the behavior, please provide code snippets or a repository:

  1. Login a react native app
  2. Close the app
  3. Reopen the app

Expected behavior

Session should be restored when opening the app

Screenshots

If applicable, add screenshots to help explain your problem.

System information

  • OS: macOS
  • React Native 0.63.4
  • @supabase/gotrue-js: 1.12.4
  • @supabase/postgrest-js: 0.28.0
  • @supabase/realtime-js: 1.0.6

Additional context

If I uncomment these lines then everything works as expected. (https://github.com/supabase/gotrue-js/blob/master/src/GoTrueClient.ts
)

//should be handle on _recoverSession method already
this._saveSession(currentSession)
this._notifyAllSubscribers('SIGNED_IN')

`update` allows users to set empty or otherwise invalid passwords

Bug report

Describe the bug

When logged in, the user can update its password to an empty or invalid password, which would've been rejected at sign up.

To Reproduce

Using this code:

async function changePassword(alertData: any) {
  const { error } = await supabase.auth.update({
    password: alertData.password,
  });
  if (error) {
    setErrorMessage(error.message);
    setShowErrorAlert(true);
  }
}
  1. Call changePassword(alertData) with alertData.password = ""
  2. Check the network request in Dev Tools
  3. See a successful PUT request

Expected behavior

When using an empty or invalid password, either the client library or the server should return an error.

System information

  • OS: Pop_OS! 20.10
  • Browser (if applies): Firefox 87.0
  • Version of supabase-js: 1.6.0
  • Version of Node.js: 14.0.0

Error after leaving app open

Each time I leave the app open for a few hours, I come back to this error:

TypeError: undefined is not an object (evaluating 'this.currentSession')
- node_modules/react-native/Libraries/LogBox/LogBox.js:148:8 in registerError
- node_modules/react-native/Libraries/LogBox/LogBox.js:59:8 in errorImpl
- node_modules/react-native/Libraries/LogBox/LogBox.js:33:4 in console.error
- node_modules/expo/build/environment/react-native-logs.fx.js:27:4 in error
- node_modules/react-native/Libraries/Core/ExceptionsManager.js:104:6 in reportException
- node_modules/react-native/Libraries/Core/ExceptionsManager.js:171:19 in handleException
- node_modules/react-native/Libraries/Core/setUpErrorHandling.js:24:6 in handleError
- node_modules/expo-error-recovery/build/ErrorRecovery.fx.js:9:32 in ErrorUtils.setGlobalHandler$argument_0
- node_modules/regenerator-runtime/runtime.js:63:36 in tryCatch
- node_modules/regenerator-runtime/runtime.js:293:29 in invoke
- node_modules/regenerator-runtime/runtime.js:63:36 in tryCatch
- node_modules/regenerator-runtime/runtime.js:154:27 in invoke
- node_modules/regenerator-runtime/runtime.js:164:18 in PromiseImpl.resolve.then$argument_0
- node_modules/react-native/node_modules/promise/setimmediate/core.js:37:13 in tryCallOne
- node_modules/react-native/node_modules/promise/setimmediate/core.js:123:24 in setImmediate$argument_0
- node_modules/react-native/Libraries/Core/Timers/JSTimers.js:130:14 in _callTimer
- node_modules/react-native/Libraries/Core/Timers/JSTimers.js:181:14 in _callImmediatesPass
- node_modules/react-native/Libraries/Core/Timers/JSTimers.js:441:30 in callImmediates
- node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:387:6 in __callImmediates
- node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:135:6 in __guard$argument_0
- node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:364:10 in __guard
- node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:134:4 in flushedQueue
* [native code]:null in flushedQueue
* [native code]:null in invokeCallbackAndReturnFlushedQueue

The error comes from SupabaseClient.js

const constants_1 = require("./lib/constants");

Is this a known issue where currentSession times out while the user has the app open. Is there a work around?

Invalid JWT on refresh

Bug report

Describe the bug

I am using a SSR authentication flow with Sapper. Since I do not want the user in localStorage, I authenticate via a server route, using the supabase.auth.api functions. When I login, it redirects to the dashboard fine, which confirms me that the JWT is valid, since I check that on page load. However, once I refresh the page, I get this error message: Error: Invalid token: token contains an invalid number of segments.

To Reproduce

Here is my code, as I it may be possible the error is in my code/I am missing something.

// /server.ts

[... other code]
sapper.middleware({
	session: async (req, res) => {
		const results = await supabase.auth.api.getUser(req.cookies.supaCookie);
		console.log(results); // The error messages are logged by this line, but on login it logs the user correctly.
		// Gets the cookies set by Supabase and checks if it's valid
		if (results.user) {
			return {
				userToken: req.cookies.supaCookie
			};
		}
		res.clearCookie('supaCookie'); // This oddly doesn't clear the cookie, but whatever really.
		return {
			userToken: false
		};
	}
})
// /src/api/auth.js - called by /signin.svelte

export async function post(req, res) {
	const { email, password, authType } = req.body;
	let userData = await _signin(email, password);

	if (userData) {
		res.cookie('supaCookie', userData.access_token, { maxAge: userData.expires_in });
		return res.end(JSON.stringify({ success: true }));
		// the /signin.svelte page then takes that result and redirects to /dashboard
	}
	return res.end(JSON.stringify({ success: false }));
}

async function _signin(email, password) {
	const { data, error } = await supabase.auth.api.signInWithEmail(email, password);

	if (error) {
		console.log('Error signing in: ', error.name, error.message);
		return false;
	}

	return data;
}
<!-- /dashboard -->

<script>
	import { stores } from '@sapper/app';
	const { session } = stores(); // Technically unneeded, comes from a tutorial I am loosely basing this on.
</script>

<script context="module">
	export async function preload(page, session) {
		let { userToken } = session;
		if (!userToken) return this.redirect(302, '/signin');
		return userToken;
	}
</script>

Expected behavior

The JWT should still be valid on refresh. In fact, jwt.io doesn't return any error about the JWT.

System information

  • OS: Ubuntu 20.04 (WSL 2)
  • Browser (if applies): Firefox
  • Version of supabase-js: 1.3.3
  • Version of Node.js: 12.19.0

Additional context

I may be misunderstanding the way a JWT works, so if it is my misunderstanding, I am sorry about it! I'm just a bit confused as it seems the token should still be valid. I have only tried this on my development machine, if that changes something.

redirectTo does not seem to work (because it's forbidden)

Bug report

Describe the bug

I've been going a little insane over this redirectTo parameter with auth calls. Whatever I tried passing was not getting sent to the server, despite the fix for #63.
It turns out that setting the Referer header programmatically is not allowed: https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name

To Reproduce

See the steps in #63, but upgrade supabase-js to 1.4.1.
The magic link redirect_to parameter contains the calling url, not the one passed to gotrue's redirectTo.
A warning is issued in firefox's console: Attempt to set a forbidden header was denied: referer

Expected behavior

The redirection link in the email should match what is passed to the auth call. I'd suggest either passing a different, custom header, or passing the url as part of the body of the request, whichever requires the least changes on the server side.

System information

  • OS: debian/linux
  • Browser firefox 84
  • Version of supabase-js: 1.4.1
  • gotrue-js version 1.11.2

RFC: change localstorage in constructor to synchronous request

Feature request

Is your feature request related to a problem? Please describe.

https://github.com/supabase/gotrue-js/blob/cccc4f49f6de8067b68c6892fa471a5dd544d4cb/src/GoTrueClient.ts#L57

Since the call to _recoverSession is async, it means that currentSession and currentUser isn't populated by the constructor, and that clients need to wait for the stateChangeEmmitter to fire.

This is causing a lot of problems [1] [2]

Describe the solution you'd like

LocalStorage is synchronous, so we can remove the await. However we will need a way to handle it for ReactNative.

Is there a way we can handle it for both situations?

Additional context

Side note - I have seen some people complaining that globalThis is undefined. This is also related to storage. Should this also be updated to something that works everywhere?

https://github.com/supabase/gotrue-js/blob/cccc4f49f6de8067b68c6892fa471a5dd544d4cb/src/GoTrueClient.ts#L10

Get URL from session

Feature request

Is your feature request related to a problem? Please describe.

I sometime need to pass a session by url, there is function to parse the URI components:
https://github.com/supabase/gotrue-js/blob/286fe863b7d7429dce8bc86984a3ef81771fe7b8/src/GoTrueClient.ts#L211-L253
but no function to generate the URI components from a session.

Describe the solution you'd like

Here is something I think would work, although not tested:

export default class GoTrueClient {
  /**
   * Gets a string from session data
   * @param options.session Use this session instead of the active session
   */
  getUriComponentsFromSession(options?: {
    session?: Session
  }): { sessionString: string | null; error: Error | null } {
    try {
      const session = options?.session ?? this.currentSession
  
      if (!session?.access_token) throw new Error('Not logged in.')
  
      const sessionString = [
        `access_token=${encodeURIComponent(session.access_token)}`,
        `expires_in=${encodeURIComponent(session.expires_in)}`,
        `refresh_token=${encodeURIComponent(session.refresh_token)}`,
        `token_type=${encodeURIComponent(session.token_type)}`,
      ].join('&')
  
      return { sessionString, error: null }
    } catch (error) {
      return { sessionString: null, error }
    }
  }
}

If this is a desirable feature I can submit a PR.

Export types for use by downstream librarys

Feature request

Is your feature request related to a problem? Please describe.

Yes. Because the gotrue-js types are not exported, they can't be used in downstream library.

Describe the solution you'd like

Export all types by default.

Describe alternatives you've considered

None

Additional context

This feature requested will be followed by an additional request to the supabase-js library so that all necessary types (gotrue, supabase, realtime) are available in TypeScript projects.

Not working with React Native

This is a Hacktoberfest task!
You can read more about Hacktoberfest on our blog and see all tasks on our Hacktoberfest Project Board.

Summary

This library isn't working with React native because it tries to access localstorage.

Details

In ReactNative, there is no localstorage, it is instead AsyncStorage. We need to rewrite any calls to localstorage to guard against this. It might be better to split out all storage calls to a helper file so that we can extend it in the future.

redirectTo is not used in GoTrueApi

Bug report

Describe the bug

If I call supabase.auth.signIn({email}, {redirectTo: "someurl"}) the redirectTo argument is passed down to https://github.com/supabase/gotrue-js/blob/6332b080c8a50a7120106288303c043a0abf8334/src/GoTrueApi.ts#L90 but then on https://github.com/supabase/gotrue-js/blob/6332b080c8a50a7120106288303c043a0abf8334/src/GoTrueApi.ts#L94 it's not actually used.

To Reproduce

Steps to reproduce the behavior, please provide code snippets or a repository:

  1. call supabase.auth.signIn({email}, {redirectTo: "someurl"})
  2. See that the browser calls /magiclink on supabase, but the redirectTo parameter is not there.

Note that there is also something weird with the Referer header that is not set correctly for me (I'm just seeing the hostname), which means that the auth flow is completely broken: the configured site URL is not used, Referer is wrong and redirectTo is lost in the call stack. I'll open a separate issue about the referer.
Edit: I'm trying to figure out where this referer issue is coming from, I seem to only see it on localhost, so it might be related to the dev server (I'm using svelte and sapper) somehow.

Expected behavior

It should be there, if we want to get it back out on the other end :)

RFC: How to handle email updates

RFC

We need to figure a clean strategy for handling email updates

When a user signs up, GoTrue returns a user

{
  "app_metadata": Object {
    "provider": "email",
  },
  "aud": Any<String>,
  "confirmed_at": Any<String>,
  "created_at": Any<String>,
  "email": user@email.com,         // user email
  "id": Any<String>,
  "last_sign_in_at": Any<String>,
  "role": "",
  "updated_at": Any<String>,
  "user_metadata": Object {},
}

then if they update their email using PUT /user, a completely new field is added

{
  "app_metadata": Object {
    "provider": "email",
  },
  "aud": Any<String>,
  "confirmed_at": Any<String>,
  "created_at": Any<String>,
  "email": user@email.com,         // original email
  "new_email": "[email protected]",            // updated email
  "email_change_sent_at": "2021-02-15T07:41:15.8564305Z", // new field
  "id": Any<String>,
  "last_sign_in_at": Any<String>,
  "role": "",
  "updated_at": Any<String>,
  "user_metadata": Object {},
}

If we update again, the original email stays, but the new_email field is updated.

{
  "app_metadata": Object {
    "provider": "email",
  },
  "aud": Any<String>,
  "confirmed_at": Any<String>,
  "created_at": Any<String>,
  "email": user@email.com,         // original email
  "new_email": "[email protected]",            // updated email - changed
  "email_change_sent_at": "2021-02-15T07:41:15.8699128Z",
  "id": Any<String>,
  "last_sign_in_at": Any<String>,
  "role": "",
  "updated_at": Any<String>,
  "user_metadata": Object {},
}

I'm sure there is a reason why it was done this way in GoTrue, but I can't figure out why.

Should we standardise the object in this library so that it always returns the current email?

  async function getUser(
    jwt: string
  ): Promise<{ user: User | null; data: User | null; error: Error | null }> {
    try {
      const data: any = await get(`${this.url}/user`, { headers: this._createRequestHeaders(jwt) })
      let user = data 
      if (data.new_email) user.email = data.new_email
      return { user, data, error: null }
    } catch (error) {
      return { user: null, data: null, error }
    }
  }

setting current session after successful third party login

after a successful third party authentication the user will be redirected to:

<your-site-url>/#access_token=xyzabc&expires_in=3600&refresh_token=abcdef&token_type=bearer
<your-site-url>/?access_token=xyzabc&expires_in=3600&refresh_token=abcdef&token_type=bearer

we need a mechanism (if supabase-js) is present on the page -> to check the page url on startup and if these values are present - to use them to set the current session

I find the # slightly strange, if it's better I can also alter gotrue to use a ? instead so it's more like parsing query params

using in node context w/policy

Jump to the Solution specification

problem:

tried using RLS in node context, is there a step I'm missing?

create table public.posts (
    id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    uuid uuid
);
alter table posts enable row level security;
create policy "users can insert only own posts" on public.posts for insert with check (auth.uid() = uuid);
const { createClient } = require("@supabase/supabase-js");
const supabase = createClient(
  "https://REF.supabase.co",
  "KEY"
);
const main = async () => {
  supabase.auth
    .signIn({ email: "[email protected]", password: "11" })
    .then(() => {
      supabase
        .from("posts")
        .insert([{ uuid: supabase.auth.session().user.id }])
        .then(console.log)
        .catch(console.log);
    })
    .catch(console.log);
};
main();

but getting:

{
  error: {
    hint: null,
    details: null,
    code: '42501',
    message: 'new row violates row-level security policy for table "posts"'
  },
  data: null,
  status: 403,
  statusText: 'Forbidden',
  body: null
}

React Native magic link sign in

Feature request

Is your feature request related to a problem? Please describe.

I'm building a React Native app, and I'd like the email magic link sign in to work. I can forward the query params over to the application, but the API for initializing the session seems limited

Describe the solution you'd like

There is a method getSessionFromUrl: https://github.com/supabase/gotrue-js/blob/0c75bf9e33f5e19403c57113a7a5d27359dd9492/src/GoTrueClient.ts#L258-L262`

It could be extended or refactored to allow a user like myself to pass in the auth query param data manually.

Describe alternatives you've considered

I could technically do this right now in userland by copying some of this logic manually and calling the "private" methods such as this._saveSession(session). But that seems nasty :)

Additional context

I'm happy to help out with a PR if there's not already a solution I'm missing. Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.