nhost / nhost-js-sdk Goto Github PK
View Code? Open in Web Editor NEWNhost JS SDK to handle Auth and Storage with Nhost.
Home Page: https://docs.nhost.io/libraries/nhost-js-sdk
Nhost JS SDK to handle Auth and Storage with Nhost.
Home Page: https://docs.nhost.io/libraries/nhost-js-sdk
As explained in Hasura's blog post about JWTs, it seems preferable to store the refresh token in a cookie rather than localStorage.
Let me know if you need help on this.
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.
In the SDK repo README, and the nhost docs, please include all the SDK [config options] (https://github.com/nhost/nhost-js-sdk/blob/master/src/index.ts#L23-L30).
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.
and on 401 only.
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?
introduce SSR init state [null, false]
for signedIn.
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,
})
Local authentication functions such as login and register don't work, most likely due to the introduction of oAuth authentication. Login and register endpoints need to be updated
Once a user has been successfully logged in, using the login()
method, the nhost API will include the jwt_expires_in
in the response payload. If this is used, to override the default of the refresh_interval_time
in the SDK config, the client and nhost will be closer aligned.
We should set Github Actions up for this repository to test our changes.
And give a better error message if file
is not a File.
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:
auth.getJWTToken()
halts return, or returns null, until the JWT Token has been refreshedJust my ideas above, might be other ways. Looking forward to hearing what your thoughts are on this one.
Current NHB+ has a route defined as router.get('/activate', activateAccount)
and reads the ticket from query whereas the sdk is doing a POST with a body and the ticket inside.
Hi there,
in case the existing password is wrong the error should be specific not generic.
could we send these through somehow?
thanks,
C
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?
I am using nhost-js-sdk 3.0.0, but the refresh lock does not seem to work.
Lines 559 to 563 in 43c0934
Sometimes the refreshSession gets called twice, the second one obviously fails, thereby automatically logging the user out. Do others experience the same problem?
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)
Hi @elitan ,
As jsonwebtoken
package didn't work in React-Native and it seems that you are only need jwt decode capability please switch jsonwebtoken
to jwt-decode
package then we can use nhost in React-Native mobile app more simpler.
https://github.com/auth0/jwt-decode
otherwise, it will show in the NextJS logs.
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>
);
}
ex if base_url
is used, tell that the new variable name is baseURL
etc.
We don't want to save undefined
or something. It will mess up the autoLogin procedure and could star throwing warning.
@elitan Hi man,
I think it can be useful specially when the network is unstable:
https://github.com/tiaanduplessis/tenacious-fetch
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
if possible.
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.
Currently, using auth.isAuthenticated()
returns:
true
means users is logged infalse
means users is not logged innull
means that nhost-js-sdk is trying to login the user, but don't know yet if the user is logged in or not.
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.
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?
for v3.0.0
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)?
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:
Line 313 in 8498dc7
unsubscriber function will remove the custom onAuthStateChanged function that was inserted.
https://github.com/nhost/nhost-js-sdk/blob/master/src/Auth.ts#L100-L102
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>
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.
ssr: typeof window === 'undefined'
I think it's very useful if we can set storage as an option parameter.
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(...)
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
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.
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?
will throw errors if localStorage
is not present.
I spend some time trying to get this right. Here are my findings:
useCookies
setup parameter to true, by default is false.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.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.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
.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.
After login do you have to query the user info with the given email input, or are there other ways to do this?
Would be good if there's an example using external auth providers.
The current logic seems to allow setting only one callback; setting a new one replaces the former one.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.