I stored my token to authenticate with my GraphQL API by adding a JWT to the header inside an apollo-link-states #client property.
query ClientToken() {
clientToken #client
}
I now want to use that token to authenticate my Apollo remote queries. Without using the local cache, doing the following works:
const authLink = new ApolloLink((operation, forward) => {
operation.setContext({
headers: {
Authorization: `Bearer GETMEATOKEN`
}
})
return forward(operation)
})
I'm struggeling to find a way to query this locally stored token inside this operation to add it where currently GETMEATOKEN appears.
Anyone has a suggestion if/who to query for a locally stored property inside an ApolloLink?
Thanks for all suggestions
Related
I have a get refresh token url like this client.com/api//auth/refresh-token. but I have a hard time using this. I think it should save a refresh token in the local storage after the login. but how can I use it?
login.tsx
export const useLogin = () => {
const LoginAuth = async (data: AuthenticationProps) => {
await axios.post(baseURL + `client/auth/login`,
{
email: data.email,
password: data.password,
},
{
headers: {
"Content-Type": "application/json",
Accept: "application/json",
}
}
)
.then((res) => {
if(res.status === 200) {
console.log("success")
}
}, (err) => {
console.log(err);
})
}
return {
LoginAuth,
}
}
You should not set the refresh token in local storage, it would cause a security vulnerability, since local storage is accessible by javascript, and since refresh token is long term token (live longer than access token), what you would do is, to store access token in local storage, since access token is short termed token, storing in local storage or cookies is totally fine, and then you should make an useEffect() call in react, that check whenever the token is expired and then make the call, a small example:
import Cookies from 'js-cookie';
axios.get("ur_url_here/",data,{withCredentials:true}).then((res)=>{
Cookies.set(res.data.access) // assuming the response has the access token
}))
// now we check the expiration of access token
useEffect(()=>{
if(!(Cookies.get("access"))){
axios.get("refresh_url_here/",{withCredentials:true}).then((res)=>{
Cookies.set(res.data.access)
})
/*what you do here, is try to have a
resource/view in your backend that has
the refresh token and make request to it
so that it gives you a new access token,
because refresh token should be in cookies tagged with `httponly',
then you can send the access token to client side
as a response and set it somewhere.
*/
}
else{
//do something else
}
},[])
this is a simplified code, but should explain well the idea of refreshing a token safely.
also note, i stored access in cookies, but you can do the same and store it in local storage.
Save it in local storage
export const storeToken = async (token: string) => {
await AsyncStorage.setItem('#token', token);
};
And fetch from storage when needed
export const getToken = async () => {
return await AsyncStorage.getItem('#token');
};
You should probably fetch the token from storage when application starts or when fetching from the API and store it in state or such while using the application.
Save in web storage
Only strings can be stored in web storage
LocalStorage
Persists even when the browser is closed and reopened.
Get
const token = localStorage.getItem('token');
Set
localStorage.setItem('token', 'value')
SessionStorage
Data removed when browser closed
Get
sessionStorage.getItem('token', 'value')
Set
sessionStorage.setItem('token', 'value')
You can use LocalStorage, or SessionStorage.
export const useLogin = () => {
const LoginAuth = async (data: AuthenticationProps) => {
await axios.post(baseURL + `client/auth/login`,
{
email: data.email,
password: data.password,
},
{
headers: {
"Content-Type": "application/json",
Accept: "application/json",
}
}
)
.then((res) => {
if(res.status === 200) {
console.log("success")
window.localstorage.setItem('authToken', res.data.token);
// Same as session storage
// window.localstorage.setItem('authToken', res.data.token);
}
}, (err) => {
console.log(err);
})
}
return {
LoginAuth,
}
}
You can check here for the difference
The best way to store the refresh token is in localstorage.
Setting token in localstorage,
localStorage.setItem("token", token);
Getting token from localstorage
let token = localStorage.getItem("token");
You can also view the stored token in browser like below,
All the measures of security of web application logic process conclude by giving you access token and refresh token, then and its your responsibility to keep them safe. As long as these tokens are valid, they are only artifacts required to make an access. In fact if you look at OIDC flows the access token is not even handed to browsers in most of them because of so many known weaknesses of browsers in terms in security.
The best way to keep or store either of these tokens is to deal with them in back channel and if not then in browsers encrypt them with custom logic and store in local storage so that only your app knows how to use these tokens.
Better even to have the backend code does this part for you as you know javascript is always exposed and retrievable.
Hope this helps.
I currently have a application with Laravel + Sanctum + Vue SPA + Apollo GraphQL.
I'm trying to make a session expire just like in a normal Laravel application but i can't achieve this.
First I make a request to trigger the csrf-cookie of Sanctum on frontend:
await fetch(`${process.env.VUE_APP_API_HTTP}/api/csrf-cookie`, {
credentials: 'include'
})
It generates 2 cookies on browser:
XSRF-COOKIE and laravel_session
On login I use apollo and store the auth-token after make a login request:
const data = await apolloClient.mutate({
mutation: Login,
variables: credentials
})
const token = data.data.login.token
await onLogin(apolloClient, token)
export async function onLogin (apolloClient, token) {
if (typeof localStorage !== 'undefined' && token) {
localStorage.setItem(AUTH_TOKEN_NAME, token)
}
....
So i pass the token and cookie to apolloClient link prop, but i'm not sure if it is needed to pass the XSRF-TOKEN.
const authLink = setContext(async (_, { headers }) => {
const token = localStorage.getItem(AUTH_TOKEN_NAME)
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : '',
'XSRF-TOKEN': Cookie.get('XSRF-TOKEN'),
}
}
})
Here is the problem: The login session never expires, even with the cookie laravel_session, i already tried to pass laravel_session as a header on my link connection but it doesn't seems to work.
My Laravel session.php is set 'expire_on_close' => true to be sure i can test it i close the browser and re-open, also i'm sure the cookie is set to expire on close because it says on browser cookies info.
Any idea how can i make the laravel session work on a SPA?
If you are using cookies to manage the session, your .env file should look like this:
SESSION_DRIVER=cookie
You can also define the session lifetime below
SESSION_LIFETIME=120
Suggestion: set lifetime to 1 minute, do a login and wait to see if it expires. Let me know!
i am implementing user authentication with the help of firebase in my React project. So, I am confused over something.
I am verifying the user from firebase and then getting a token on frontend which is sent to backend via headers and verfied there once.
I read the docs and came to know that firebase token gets expired after 1 hr by default so we have to use "getIdToken" like
firebase.auth().onAuthStateChanged(async user => {
if (user) {
console.log(user, 'user123 inside firebaseAuth')
const token = await user.getIdToken()
Cookies.set('my_token', token, { domain: domain })
}
})
but how do i manage this function , do i have to call it everytime the component updates or everytime before hitting api or first time the component renders ?
The thing is i do not want this token to get expire until the user logs out himself / herself even if he is in a different component and sitting ideal for too long.
You can get the Firebase ID Token every time you are making an API call to your server:
async function callAPI() {
const user = firebase.auth().currentUser
if (user) {
const token = await user.getIdToken()
const res = await fetch("url", {
headers: {authorization: `Bearer ${token}`}
})
} else {
console.log("No user is logged in")
}
}
You could get the ID token once when the component mounts but then you'll have to deal with onIdTokenChanged to keep it updated in your state. Using the method above you'll get a valid token always.
I have nestjs application which uses typeorm and mysql. Now I would like to add firebase for authentication handling, i.e for signup, signin, email verification, forgot password etc.
Plans is create user first in firebase, then same user details will be added into mysql user table for further operaiton. So for this I am using customized middleware
#Injectable()
export class FirebaseAuthMiddleware implements NestMiddleware {
async use(req: Request, _: Response, next: Function) {
const { authorization } = req.headers
// Bearer ezawagawg.....
if(authorization){
const token = authorization.slice(7)
const user = await firebase
.auth()
.verifyIdToken(token)
.catch(err => {
throw new HttpException({ message: 'Input data validation failed', err }, HttpStatus.UNAUTHORIZED)
})
req.firebaseUser = user
next()
}
}
}
Full code is available in Github
Problem with above code is that, it always looks for auth token, const { authorization } = req.headers const token = authorization.slice(7)
However, when user first time access application, authorization header always be null.
example if user access signup page we cannot pass auth header.
please let me know how can I modify above code when user access signup page, it allows user create user firebase, then same details can be stored in database.
We can exclude the routes for which you don't want this middleware.
consumer
.apply(LoggerMiddleware)
.exclude(
{ path: 'cats', method: RequestMethod.GET },
{ path: 'cats', method: RequestMethod.POST },
'cats/(.*)',
)
.forRoutes(CatsController);
You can just next() to skip this middleware if there is no authorization. so it s okay to access sign up api when no authorization
I am pretty new to Axios and very new to OAuth and Firebase, so I'm sure I'm missing something dumb...
I am trying to create a sign in using firebase's auth provider functions & then create a user profile in my database using Axios. (I have to make a ton of other API calls based on the data I receive and it would be very convenient to just use Axios for everything.)
Here is what I have so far.
authenticate() {
var provider = new firebase.auth.GithubAuthProvider();
console.log(provider);
firebase.auth().signInWithPopup(provider)
.then((res) => {
console.log(res);
if (res.credential) {
var token = res.credential.accessToken;
}
const user = axios.create({
baseURL: fbaseUrl,
withCredentials: true, // newly added
headers: {
Authorization: `Bearer ${token}`, // cf firebase docs https://firebase.google.com/docs/reference/rest/database/user-auth
}
});
this.setState({uid: res.user.uid, useraxios: user, token: token});
}).catch((err) => {
console.log(err.message);
});
}
testPost() {
this.state.useraxios.post(`/users.json`, { id: this.state.uid, joinedOn: moment() })
.then((res) => console.log(res))
.catch((err) => console.log(err.message)); /// this errors out
}
The error I'm currently getting is that there is no 'Access-Control-Allow-Credentials' header and therefore localhost is not allowed access, which I assume is something in the Firebase rules that I have to sort through. Before I added the withCredentials: true line, I was just getting the "not allowed access" response.
I have also tried
const user = axios.create({
baseURL: `${fbaseUrl}/users/${res.user.uid}.json?auth=${token}`
});
and
firebase.auth().currentUser.getToken(true).then((token) => {
const user = axios.create({
baseURL: `${fbaseUrl}/users/${res.user.uid}.json?auth=${token}`
});
and
firebase.auth().currentUser.getToken(true).then((token) => {
const user = axios.create({
baseURL: `${fbaseUrl}`,
headers: {Authorization: token}
});
as per this stackoverflow question which returns the 401 Unauthorized error.
Posting to the database is totally fine when I have both read & write set to true, so it's not a problem with how I'm formatting the URL or something.
I am assuming there are a couple of problems, one with my axios.create config and another with my Firebase rules, but I have gone through the documentation for both and am still very much at a loss. This is a react app but I'm 98% sure the react stuff isn't the problem.
Am I at least on the right track? (Am I a fool to try to use axios for something that would be better suited to firebase's built-in methods...?) Any help would be deeply appreciated.
It is related to your functions configuration. You need to add this in your firebase functions / index.js and configure your function with cors.
const cors = require('cors')({origin: true});
For more details please refer to this url: Enabling CORS in Cloud Functions for Firebase