Code Monkey home page Code Monkey logo

nhost-js-sdk's Introduction

Deprecation notice

This repository is the source code of the Nhost V1 SDK and is deprecated and not actively maintained. New SDK version can be found on the main nhost-js repository.

Nhost JS SDK

Nhost JS SDK to handle Auth and Storage with Nhost.

Install

$ npm install nhost-js-sdk

or

$ yarn add nhost-js-sdk

Documentation

https://docs.nhost.io/libraries/nhost-js-sdk

nhost-js-sdk's People

Contributors

amal-chandran avatar dependabot[bot] avatar elitan avatar komninoschatzipapas avatar migsar avatar nunopato avatar oskarer avatar plmercereau avatar rikardwissing avatar shyndman avatar skinymonkey avatar st3phan avatar svarto avatar

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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nhost-js-sdk's Issues

client_storage has missing type AsyncStorageStatic

When using nhost-js-sdk in a React-Native project, client_storage has the wrong type, it only supports ClientStorage, however react-native AsyncStorage has type AsyncStorageStatic

UserConfig client_storage should be client_storage?: ClientStorage | AsyncStorageStatic;

Right now we need to cast it as ClientStorage but I don't think we should cast it.

Documentation on response data

Hey there,

I'm writing purescript bindings to this package. Would it be possible to have the login, register and any other relevant auth commands return the response data (as well as having documentation for what fields are available)?

E.g. Here:

https://github.com/nhost/nhost-js-sdk/blob/master/src/auth.js#L202
https://github.com/nhost/nhost-js-sdk/blob/master/src/auth.js#L241 (would be nice to have the data returned after doing init session)

It might also be nice to have some notion of getting a current user profile or something (unless that's something you would hit hasura for normally)?

module has no exported defaults

Hello,

import nhost from 'nhost-js-sdk'
console.log(nhost) // <-- undefined

Of course, things work with import { createClient } from 'nhost-js-sdk', but I would allow to code like this:

import nhost from 'nhost-js-sdk'
nhost.createClient(...)

change password endpoint response

Hi there,

in case the existing password is wrong the error should be specific not generic.
could we send these through somehow?

thanks,
C

errors apollo-client: "no such type 'uuid'" apollo-link: "no subscriptions exist"

I'm trying to set up nhost with apollo client connecting to hasura, but I'm getting following errors:

// graphql query call
apollo-client.js:63 Uncaught (in promise) 
Error: GraphQL error: no such type exists in the schema: 'uuid'
//graphql subscription call
apollo-link.js:71 Uncaught 
{extensions: {…}, message: "no subscriptions exist"}
extensions: {path: "$", code: "validation-failed"}
message: "no subscriptions exist"

I'm kinda stuck cause I haven't had any luck googling.
Could there be something wrong setting the JWT token?
(auth.getJWTToken() seems to give a valid token)
(the gql queries are valid cause they work ok with authentication disabled)

// configuration
import ApolloClient from "apollo-client"
import { InMemoryCache } from "apollo-cache-inmemory"
import { WebSocketLink } from "apollo-link-ws"
import { split } from "apollo-link"
import { HttpLink } from "apollo-link-http"
import { getMainDefinition } from "apollo-utilities"
import { auth } from "@/js/nhost"

const JWT = auth.getJWTToken()

const headers = {
  "content-type": "application/json",
  Authorization: "Bearer " + JWT,
}
console.log("headers", headers)
const getHeaders = () => {
  return headers
}

const cache = new InMemoryCache()

const wsLink = new WebSocketLink({
  uri: "ws://localhost:8080/v1/graphql",
  options: {
    reconnect: true,
    lazy: true,
    connectionParams: () => {
      return { headers: getHeaders() }
    },
  },
})

const httpLink = new HttpLink({
  uri: "http://localhost:8080/v1/graphql",
  headers: getHeaders(),
})

const link = split(
  ({ query }) => {
    const { kind, operation } = getMainDefinition(query)
    return kind === "OperationDefinition" && operation === "subscription"
  },
  wsLink,
  httpLink
)

export const apollo = new ApolloClient({
  link,
  cache,
})

`/refresh_token` gets called twice when reloading a page

Hi, using revisions 3.0.0 to 3.1.1-0, after log in, if a user refreshes the page /refresh_token will be called twice, causing the second call to return a 401.

Screenshot 2021-09-14 at 16 47 02

Also, onAuthStateChanged is not called after the /logout endpoint is called.

putString method returns sample image.

When using the putString method to upload a base64 string a sample image is uploaded instead. Meaning that in Nhost storage you get this tiny white square instead of the image you tried to upload. From the Storage.js file in the nhost-js-sdk it seems that a sample image is hardcoded. Can be seen in Storage.prototype.putString from line 106.

AUTO_ACTIVATE_NEW_USERS is false but user still get logged in on register

Hello,

Recently I stumbled upon a problem with the registry function from nhost SDK. When I register a user, it used too not automatically log me in because I sat AUTO_ACTIVATE_BASE USERS to false. But now when I use the same function, it gives the user a session even if it should be null. But after logging out and try logging in it will wait for activation.

Function:


  const registerUser = async () => {
          try {
              await auth.register({email, password, options: { userData: { display_name: email, avatar_url: "profile.png", first_name: "name", last_name: "last name" }, defaultRole: "user" }});
              console.log("User is registered")
          } catch (error) {
              console.error(error);
          }
  
      }

Any reason why this is happening?

Question: Handling errors in response

Hello!

Thank you for the SDK and the hasura-backend-plus project, this has made writing new applications for user management sure simple.

I was wondering how errors are handled when using the sdk? I do not see any error examples in the documentation. Specifically, login errors or registration errors for various conditions (incorrect password, user exists.. and such)

Add CI

We should set Github Actions up for this repository to test our changes.

Call refreshToken when JWTExpired error

Hi!

I'm building out my frontend and everything is working smoothly, as long as the user stays logged in and active the tokens get refreshed automatically, but sometimes the user has been inactive for too long and when they make a GraphQL request via Apollo they get a JWTExpired back. This is expected, but I would like to add an onError method (with apollo-link-error) to refresh the JWT and make the query again.

In the source code I see that there is a refreshToken method, but it's private, so I can't call it. What's the best way to manually refresh a token?

Storage put undefined error when passing in file

Hope someone has any ideas on this!

Running a react-native project, just picked the standard expo-image-picker example and took the file uri output and tried to upload it to nhost. Got a weird undefined error:


undefined is not an object (evaluating '_nhostJsSdk.storage.put')
- node_modules/regenerator-runtime/runtime.js:45:44 in tryCatch
- node_modules/regenerator-runtime/runtime.js:274:30 in invoke
- node_modules/regenerator-runtime/runtime.js:45:44 in tryCatch
- node_modules/regenerator-runtime/runtime.js:135:28 in invoke
- node_modules/regenerator-runtime/runtime.js:145:19 in PromiseImpl.resolve.then$argument_0
- node_modules/promise/setimmediate/core.js:37:14 in tryCallOne
- node_modules/promise/setimmediate/core.js:123:25 in setImmediate$argument_0
- node_modules/react-native/Libraries/Core/Timers/JSTimers.js:146:14 in _callTimer
- node_modules/react-native/Libraries/Core/Timers/JSTimers.js:194:17 in _callImmediatesPass
- node_modules/react-native/Libraries/Core/Timers/JSTimers.js:458:30 in callImmediates
* [native code]:null in callImmediates
- node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:407:6 in __callImmediates
- node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:143:6 in __guard$argument_0
- node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:384:10 in __guard
- node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:142:17 in __guard$argument_0
* [native code]:null in flushedQueue
* [native code]:null in invokeCallbackAndReturnFlushedQueue

this is the full code:


import React, { useState, useEffect } from "react";
import { Button, Image, View } from "react-native";
import * as ImagePicker from "expo-image-picker";
import Constants from "expo-constants";
import { storage } from "nhost-js-sdk";

export default function ImagePickerExample() {
  const [image, setImage] = useState(null);

  useEffect(() => {
    (async () => {
      if (Constants.platform.ios) {
        const {
          status,
        } = await ImagePicker.requestCameraRollPermissionsAsync();
        if (status !== "granted") {
          alert("Sorry, we need camera roll permissions to make this work!");
        }
      }
    })();
  }, []);

  const pickImage = async () => {
    let result = await ImagePicker.launchImageLibraryAsync({
      mediaTypes: ImagePicker.MediaTypeOptions.All,
      allowsEditing: true,
      aspect: [4, 3],
      quality: 1,
    });

    if (!result.cancelled) {
      setImage(result.uri);
    }

    // Here I try the nhost-js-sdk storage function:
    try {
      data = await storage.put(
        "/default/test_image.jpg",
        result.uri,
        metadata,
        onUploadProgress
      );
    } catch (e) {
      console.log(result.uri);
      console.log(e);
      // handle error
    }
  };

  return (
    <View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
      <Button title="Pick an image from camera roll" onPress={pickImage} />
      {image && (
        <Image source={{ uri: image }} style={{ width: 200, height: 200 }} />
      )}
    </View>
  );
}

HttpOnly Cookies auth flow is flawed.

I spend some time trying to get this right. Here are my findings:

  1. You need to set useCookies setup parameter to true, by default is false.
  2. Auth.refreshSession public method does not work because it does not allow you to pass a refresh token, which can be null but needs to be present for Auth._refreshToken to trigger a refresh token request.
  3. Auth.getJWTToken does not work because there is no previous session, the cookie is not accessible by javascript code but it will be available to the request.
  4. The only remaining way to refresh the token is private method Auth._autoLogin since it is private you need to call it by not disabling setup parameter autoLogin, which is true by default, but the funny part is that, as stated in 2, you NEED to pass a non-falsy value as refreshToken for Auth._refreshToken to work, so you need to load the page with a dummy query string, assuming you are running on port 3000 that would be localhost:3000?refresh_token=dummy string that won't be used.
  5. To know is the user is logged in you need to register a listener for Auth.onAuthStateChanged but after that you need to call Auth.user to get the user info into the your app state.

So, it is possible to get it working, it is not documented at all, please correct me if I am wrong, but with a few caveats, like the query string. I don't think it is a bug, and I prefer not to suggest changes to make it work before knowing your thoughts about it. But this is a feature I think it is particularly useful considering that HttpOnly cookies are mentioned as recommended ways to store JWT refresh tokens.

Data returned by login() doesn't match interface Session in 3.0.0-11

I tried to use version 3.0.0-11 and login() seems to return its result in a shape that doesn't match the expected type Session.

Here's the declaration of login() and Session I can see in Auth.d.ts

login({ email, password, provider, }: types.loginCredentials): Promise<types.Session>;
export interface Session {
    jwtToken: string;
    jwtExpiresIn: number;
    refreshToken?: string;
    user: User;
}

And this is the actual data I receive from login():

{
      "data": {
        "jwt_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImVjdnd2d19fV3RSNUFzdEh1Wm00WTZYWi13aWl2VUwzOU8tR2dEV0syMmsifQ.eyJodHRwczovL2hhc3VyYS5pby9qd3QvY2xhaW1zIjp7IngtaGFzdXJhLXVzZXItaWQiOiJjN2M2MmZlYS1iNzk4LTQzMDktOGM5NS03Nzc3YjQzMDU3ZGEiLCJ4LWhhc3VyYS1hbGxvd2VkLXJvbGVzIjpbInVzZXIiXSwieC1oYXN1cmEtZGVmYXVsdC1yb2xlIjoidXNlciIsIngtaGFzdXJhLWZhbi1pZCI6Im51bGwifSwiaWF0IjoxNjEyODY3NzE5LCJleHAiOjE2MTI4NzQ5MTl9.lqnCfsgnvkvaYqvhkW94roxEdashSIo1g84NTtPKZrJqkDVmr_Rju8sZJC-cBd1K_X5C2bBzbPHqD7RVmJiFQNxySvol-M1D5wXvFUunsOqftVvSMUiW6YCfX_oloSZqFH_IFJOekZYHrwwC3h8Wz50Xf7omdN8HzXhfNTk5WkdjYwhqMDwkxs4mBWkV4BoT3t4cwo2I7DNIvPgGbI-6zxBurCoHwCtXWXShA3zI-VeW6wVD8fsH9JQLJG85oE_lIObdh6lt4aI3tA5vRaxKuguCaTI_lLohOm3tPYlAU4dE_pJO1T5TJrFokM9J5kg5YS73tYmG9y-TjH6IzZyfTw",
        "jwt_expires_in": 7200000,
        "refresh_token": "7030ee1b-e229-412f-b1d5-2ecef8535b99"
      }
}

This seems to be generated here:

return { data: res.data, user: res.data.user };

[FEATURE REQUEST] User Role helper

The current SDK methods include a helper to get the user data (auth.user()). I would be great if we could get something similar for user roles. At present I can get this by calling auth.getClaim('x-hasura-allowed-roles'), but I think a helper would be more inline with the current design?

My use case: extracting user roles on the front-end to guard navigation routes, and show/hide features based on allowed roles.

Uncaught TypeError: dist.initializeApp is not a function

I'm trying to setup a client with Svelte but I'm getting this error in the browser.
Is there anything I'm missing?

nhost.js:7 Uncaught TypeError: dist.initializeApp is not a function
    at nhost.js:7

nhost.js

import nhost from "nhost-js-sdk"

const config = {
  base_url: "http://localhost:8080/v1/graphql",
}

nhost.initializeApp(config)

const auth = nhost.auth()
const storage = nhost.storage()

export { auth, storage }

App.svelte

<script>
  ....
  import { auth } from "@/js/nhost";
</script>

Error when importing nhost-js-sdk in react-native

I'm trying to configure auth in an Expo (v.39.0.2) project but am running into issues when importing the nhost-js-sdk package. See picture below. The esm module do exists in the node_modules folder and I've tried the instructions several times.
Any ideas? Looks like a dependency on the blob package.

nhost_error

API Response object

Hi @elitan ,
I think it would be nice to have the public methods of this api return an object of {T,Error} , or a tuple of [T,Error] like you have with supabase, instead of throwing within the catch block . I think the experience is better and provides better user experience .

try{
       const T=docSomeWork(args);       
      
      if(T condition not met)
      throw 'T is not valid or something'; //can still throw exception here 
     return {T,null};
 }catch(error){
     return {null,error}
}

when calling this API method , i can then

const {T,error}=await nhostClient....();
if(error) {
  //log, friendly alert message etc
return;
}
//or use result

Use promises for isAuthenticated()

Currently, using auth.isAuthenticated() returns:

  • true means users is logged in
  • false means users is not logged in
  • null means that nhost-js-sdk is trying to login the user, but don't know yet if the user is logged in or not.

Source

It would be better I think if instead of returning null, it returned a promise which eventually resolved to either true or false.


Usecase:

I'm using the Vue router, which can have a beforeEach navigation guard:

router.beforeEach((to, _, next) => {
  console.log(auth.isAuthenticated())
  if (!auth.isAuthenticated() && to.meta.auth) {
    next({ name: 'login' })
  } else {
    next()
  }
})

If I refresh the page, auth.isAuthenticated() returns null, and then eventually true - however by this point it's redirected to the login page.

I could use await auth.isAuthenticated() if it returned a promise, which would solve the issue.

Cookie is not allowed

When running the latest docker image, with default config COOKIE_SECURE, COOKIE_SAME_SITE, I get the following response when calling auth.login(email, password).

{"statusCode":400,"error":"Bad Request","message":"\"cookie\" is not allowed"}

Have tried both use_cookies: false and without the param:

const config = {
    base_url: process.env.REACT_APP_BACKEND_ENDPOINT,
    use_cookies: false,
}

Anything obvious I may be missing?

Refresh lock seems to not work

I am using nhost-js-sdk 3.0.0, but the refresh lock does not seem to work.

nhost-js-sdk/src/Auth.ts

Lines 559 to 563 in 43c0934

// set lock to avoid two refresh token request being sent at the same time with the same token.
// If so, the last request will fail because the first request used the refresh token
if (this.refreshTokenLock) {
return;
}

Sometimes the refreshSession gets called twice, the second one obviously fails, thereby automatically logging the user out. Do others experience the same problem?

Setting a timer for a long period of time

Issue

Auth.js sets a long timer like this

JWTExpiresIn = session.jwt_expires_in;
  refreshIntervalTime = this.refreshIntervalTime
      ? this.refreshIntervalTime
      : Math.max(30 * 1000, JWTExpiresIn - 45000);
  this.refreshInterval = setInterval(this._refreshToken.bind(this), refreshIntervalTime);

To which React Native complains with this message

Setting a timer for a long period of time, i.e. multiple minutes, is a performance and correctness issue on Android as it keeps the timer module awake, and timers can only be called when the app is in the foreground. See https://github.com/facebook/react-native/issues/12981 for more info.
(Saw setInterval with duration 855000ms)

This is an issue referred here Timer Issue

I am not sure how to solve this, any pointers?

How to reproduce
Just use Auth to login in a managed expo or React Native project.

Upgrade error message legibility

Right now the login and register functions return the standard axios errors, and if you want the messages from the errors you have to drill down into the promise returned error like so:

          auth
            .register(email, password)
            .catch((e) => {
              alert(e.response.data.message);
            });

Could we simplify this and also clean up the error response to be something more legible, perhaps even customizable?

JWT Refresh across multiple tabs

Hi,
Does nhost-js-sdk support refreshing JWTs across multiple tabs?
When I've got more than one tab open on my app it seems to log itself out constantly and I believe this is due to the refresh toking becoming out of sync across tabs.

Thanks

Improve robustness of expired token handling

I have an edge case in React Native where when a notification is clicked by a user, that navigates quickly to a specific screen, then if the app is backgrounded and the JWT token has already expired then that expired JWT token is used in that first request. Resulting in a "JWT token expired" error from Hasura.

I suspect this is due to auth.getJWTToken() pulls the JWT token from storage before it is updated or refreshed through the refresh token.

There are potential solutions to this that I can think of:

  • [Backward compatible] Expose a method to programmatically refresh the JWT Token with the refresh token, this will allow the client app to handle these errors by retrying the request after forcing a JWT Token refresh
  • [Potentially breaking change] Make it so that auth.getJWTToken() halts return, or returns null, until the JWT Token has been refreshed

Just my ideas above, might be other ways. Looking forward to hearing what your thoughts are on this one.

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.