I'm sending JWT tokens accross requests for authorization, however I can't seem to get the token decode each time. It works with one method but not the other. The first snippet gives a "decoded" token result from the server side, however the second one doesn't.
public async getAllUsers(req: Request, res: Response) {
try {
const payload = req["decoded"]; // gives the token decoded
if (payload) {
let users: ILoginResult = await UserData.getAllUsers(payload);
res.status(users.status).send(users.result);
}
} catch (e) {
res.status(500).send({ error: e.toString() });
}
}
public async getAccountDetails(req: Request, res: Response) {
try {
const user = req["decoded"]; // always undefined
let details: IDetails = await AccountData.getAccountDetails(name);
res.status(200).send(details);
} catch (e) {
let err = e.toString();
res.status(500).send({ error: err });
}
}
The request from postman are included a bearer token which is provided at login and used throughout other parts of the app. Not sure why it works in the one but not the other. Would really appreciate if someone could better explain what's going on here and/or provide tips, advice, suggestions.
edit - adding request details
get request to: http://localhost:5000/api/v1/account
with a token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWRtaW4iLCJpYXQiOjE1Nzc5OTUwMjUsImV4cCI6MTU3ODE2NzgyNSwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdCJ9.--msLba1VPs4Nv_B9YL6fk2DFHkQCgiVvDJFPt_UnDk
The decoded property was used in a tutorial I was following that seemed to be added from the server side but was poorly explained and I haven't found a good alternative/explanation. I don't think it has any middleware either. Very much open to alt methods.
Thanks to the suggestions from the comments I was able to find a missing piece in the route that creates the decoded property which is being used here. By adding the middleware to the router the request works as expected:
import express from "express";
import UserController from "../controllers/UserController";
import valid from "../utils/ValidateToken";
export default (router: express.Router) => {
router
.route("/users")
.post(UserController.addUser)
.get(valid.validateToken, UserController.getAllUsers);
router.route("/login").post(UserController.loginUser);
router.route("/account").get(valid.validateToken, UserController.getAccountDetails);
};
The valid.validateToken was missing which is the bit that generates the decoded object from the JWT being passed. Moral of the story, always double check everything. Thanks to all who commented/answered!
Related
In the Apollographql documentation it states:
The onError link can retry a failed operation based on the type of GraphQL error that's returned. For example, when using token-based authentication, you might want to automatically handle re-authentication when the token expires.
This is followed up by their sample code:
onError(({ graphQLErrors, networkError, operation, forward }) => {
if (graphQLErrors) {
for (let err of graphQLErrors) {
switch (err.extensions.code) {
// Apollo Server sets code to UNAUTHENTICATED
// when an AuthenticationError is thrown in a resolver
case "UNAUTHENTICATED":
// Modify the operation context with a new token
const oldHeaders = operation.getContext().headers;
operation.setContext({
headers: {
...oldHeaders,
authorization: getNewToken(),
},
});
// Retry the request, returning the new observable
return forward(operation);
}
}
}
// To retry on network errors, we recommend the RetryLink
// instead of the onError link. This just logs the error.
if (networkError) {
console.log(`[Network error]: ${networkError}`);
}
});
My question is in regards to the getNewToken(), as no code was provided for this function, I want to know (assuming this is another request to the backend and I am not sure how it could not be), if you are able to and or supposed to use query/mutation in graphql or make the request through axios for example.
One problem, if it can/should be a graphql query or mutation, is to get the new token, the onError code is defined in the same file as the ApolloClient as ApolloClient needs access to onError, thus when trying to implement this as retrieving a new token through a graphql mutation I got the following error:
React Hook "useApolloClient" is called in function "refresh" that is
neither a React function component nor a custom React Hook function.
After trying to useQuery/useMutation hook and realizing I cannot outside of a react component and at the top level I found this post whose answers suggested you can use useApolloClient.mutate instead but I still ran into issues. My code was (and tried multiple iterations of this same code like useApolloClient() outside of the function and inside etc.):
const refresh = () => {
const client = useApolloClient();
const refreshFunc = () => {
client
.mutate({ mutation: GET_NEW_TOKEN })
.then((data) => {
console.log(data);
})
.catch((err) => {
console.log(err);
});
};
refreshFunc();
};
I could capitalize Refresh but this still would not work and would break the rules of hooks.
And to clarify all the above would do is I would replace the console.logs with setting session storage to the retrieved new token and then re trying the original request with onError.
Now in another post I found when looking into this, the users getNewToken request was a rest request using axios:
const getNewToken = async () => {
try {
const { data } = await axios.post(
"https://xxx/api/v2/refresh",
{ token: localStorage.getItem("refreshToken") }
);
localStorage.setItem("refreshToken", data.refresh_token);
return data.access_token;
} catch (error) {
console.log(error);
}
};
Now from my understanding, if I wanted to implement it this way I would have to change my backend to include express as I am only using apolloserver. Now I could definitely be wrong about that as my backend knowledge is quite limited and would love to be corrected their.
So my question is, what is the best way to do this, whether natively using graphql queries/mutations (if possible), doing it with axios, or maybe their is another best practice for this seemingly common task I am unaware of.
I'm having problems when I deploy my alexa skill to google cloud functions.
Initially, I'm using the following code.
My skill.js file
//imports here...
Router.post("/:locale", async (req, res) => {
const requestEnvelope = JSON.stringify(req.body);
try {
await new SkillRequestSignatureVerifier().verify(requestEnvelope, req.headers);
await new TimestampVerifier().verify(requestEnvelope);
} catch (err) {
console.log(`Error with message: ${err.message}`);
console.log(`Error object: ${JSON.stringify(err)}`);
return res.status(400).send(err.message);
}
const responseASK = await skill.invoke(req.body);
return res.status(200).send(responseASK);
});
module.exports = Router;
In my server.js file
const skillRoute = require("./skill");
const express = require("express");
const server = express();
server.use("/", skillRoute);
exports.server = functions.https.onRequest(server);
Everything goes well with the deploy, including, I can invoke my alexa skill normally. But, when running the validations for distribution, I get the following problem:
The skill is rejecting the request when executed with additional properties in the JSON request.
When we invoke the skill with additional parameters, the skill is rejecting it when we expect this to be accepted. Future versions of the Alexa Skills Kit may add new properties to the JSON request and response formats, while maintaining backward compatibility for the existing properties. Your code must be resilient to these types of changes. For example, your code for de-serializing a JSON request must not break when it encounters a new, unknown property. Please ensure that your code can handle new attributes and does not break when it encounters new properties in the JSON request.
Documentation Help:
Request and response JSON reference: Click here
On my server console I get the following errors:
Error with message: request body and signature does not match
and:
Error object: {"name":"AskSdk.SkillRequestSignatureVerifier Error"}
It seems to me that it may be the result of some ask-sdk-core update, as I have other servers working with the same code.
I have written the following function in js to verify a ReCAPTCHA token:
export async function validateHuman(token) {
const secret = "funny_little_secret";
const response = await fetch(
"https://www.google.com/recaptcha/api/siteverify?secret=" +
secret +
"&response=" +
token,
{ method: "POST" }
);
const data = response.json();
return data.success;
}
For reasons I do not understand, I get a fetch error while running this. I have looked in to a few other versions of the functions but have not yet found a solution.
Moreover, the token is correct and is produced according to the documentation, so I am wondering whether there is something wrong about my secret, as I have seen others use something along the lines of process.env.secret, which I have tried without success, or it there is something I do not understand about the fetch function.
This is the complete error:
Uncaught TypeError: Failed to fetch
validateHuman # http://localhost:3000/static/js/bundle.js:3384:26
thanks in advance
I'm working on a personal project that will allow users to find new books based on their preferences for the genre. The database I'm using is MongoDB. However, while I'm able to get all the data on the backend using Postman, I can't get it properly displayed on the frontend. At the moment, I'm just trying to get the data sent to the front end and at least console.log'd but it isn't making it that far.
Here is the code in the routes file.
router.get('/books/:genre', bookBuilder.get_some_books)
Here's the code on the backend that the routes file is pointing to and is working:
exports.get_some_books = async function (req, res) {
let { genre } = req.params;
try {
let books = await Book.find({"genre": genre});
if (books) {
res.json(books)
} else {
res.status(404).send({error: 'Not Found'});
}
} catch (err) {
res.status(500).send({error: err.message});
}
}
Here's my code on the frontend that is not working.
async getEverything() {
try {
let pbBooks = await axios.get(`/books/`, {
method: 'GET',
headers: {'Content-Type': 'application/json'},
params: {
genre: 'PB'
}
})
if (pbBooks) {
console.log(pbBooks)
} else {
this.$router.push('/Error');
}
} catch (err) {
console.log(`Network error: ${err.message}`)
}
}
My code stack is Vue.js, Express.js, Node.js and Axios. On the frontend, I've tried making the inner code of axios.get() into '/books/PB' and then tried getEverything(genre) along with /books/${genre} but neither seems to be working.
The error I am getting is a 404 Request Failed error that is from the catch block in the getEverything() function. I'm not sure why the frontend is unable to get the data when the backend works just fine. Is there anything I'm doing wrong?
404 is the HTTP status code for Not found, which implies there is no route setup on localhost for /books. Actually, /books would route to your app, not the backend (unless you have a proxy setup on your app server that redirects to the backend).
If a proxy were involved, it's likely misconfigured. Otherwise, the target URL in the Axios request should be <backend_url>/books (e.g., http://localhost:9999/books with the back running locally on port 9999, and the app on some other port).
Change
let pbBooks = await axios.get(`/books/`, {
...
to
let genre = "PB"
let pbBooks = await axios.get(`/books/${genre}`, {
method: 'GET',
headers: {'Content-Type': 'application/json'}
})
reason is the params part of the config object is converted to query strings (/books?genre=PB) instead of /books/PB, which is what the backend is expecting
More: https://masteringjs.io/tutorials/axios/get-query-params
I'm trying to verify if the user was able to complete the registration successfully. For this, I want to get the user's token via cookies, as in the following code:
[...]
export async function getServerSideProps(ctx) {
try {
const cookies = nookies.get(ctx)
console.log(JSON.stringify(cookies, null, 2));
const token = await firebaseAdmin.auth().verifyIdToken(cookies.token)
const { email } = token
return {
props: { message: `${email} was successfully registered!`}
}
} catch (err) {
console.log(err)
return { props: { message: 'Error'} }
}
}
[...]
This function returns the following error:
errorInfo: {
code: 'auth/argument-error',
message: 'Decoding Firebase ID token failed. Make sure you passed the entire string JWT which represents an ID token. See
https://firebase.google.com/docs/auth/admin/verify-id-tokens for
details on how to retrieve an ID token.' }
I believe I must be misusing nookies.get(ctx), and I say this because the retrieved cookie (token) is equal to a "", which doesn't make much sense to me. Then, how can I properly use nookies to get that token?
I would appreciate it, if anyone could help me to find out what I'm missing out.
Thanks in advance.
EDIT: The user account is created, and the email and password are stored in the Firebase Auth. It is only this piece of code that does not work.
Since Next 10.0.4 you can get cookies without nookies
const cookies = ctx.req.cookies
try in this way, if you still getting token as empty check the way that you set cookies.