NextJs protected API with Next Auth - session

I have a problem building NextJs Web with NextAuth. I created my own API in pages/api and protected it with getSession from NextAuth. When I call the API using getServerSideProps or getStaticProps, getSession returns a null value, but when I call the API inside a component function, getSession returns the user data value. Can anyone help or have any tips?
pages/index.jsx
export async function getStaticProps(context) {
const projects = await fetchApi(`${process.env.BASE_URL}/projects`);
console.log(projects)
return {
props: {},
};
}
pages/api/projects
import { getSession } from 'next-auth/client';
async function handler(req, res) {
const projectService = new ProjectService();
let session;
let emailUser;
try {
session = await getSession({ req });
console.log(session); // null
emailUser = session.user.email;
if (!session) {
throw new AuthenticationError('No authenticated');
}
} catch (error) {
if (error instanceof ClientError) {
return res.status(error.statusCode).json(clientErrRes(error));
}
return res.status(500).json(serverErrRes(error));
}
...another code
}

You need to add headers in the fetch from the context. because you are fetching from server side.
const {req}=context
const {cookie} = req.headers
return fetch(`${process.env.BASE_URL}/projects`, {
headers: {
'Cookie':cookie
}
})

You can't have auth (per user) in getStaticProps because those pages are generated at compile time.
When you are calling the api from the react component (at runtime - from the browser) you are doing it on the behalf of the user so there is a session (cookie) there.

Related

Why does Postman show "error 404" when I try to save data into my Mongo database?

I have three files in my MVC architecture for my app.
Models folder:
I have exported the Mongoose schema and model like so:
const User = mongoose.model("User", UserSchema);
module.exports = User;
2. Controllers Folder
I have the following code in my controllers file:
const addUser = require('./../models/clientmodel');
exports.addUser = async (req, res) => {
try {
const newUser = await new addUser.create(req.body);
res.status(201).json({
status: "success",
data: {
newUser
}
});
} catch (err) {
res.status(204).json({
status: 'fail',
message: err
})
}
}
3. Routes folder
const router = express.Router();
router
.route('/')
.get (userControl.add_user)
app.post(userControl.newTour);
module.exports = router;
Why do I get the 404 error message in Postman?
How do I inser a loop?
NB: I am new to programming and learning the MERN stack.
The following code works fine in one folder:
const express = require("express");
const userModel = require("./models");
const app = express();
app.post("/add_user", async (request, response) => {
const user = new userModel(request.body);
try {
await user.save();
response.send(user);
} catch (error) {
response.status(500).send(error);
}
});
app.get("/users", async (request, response) => {
const users = await userModel.find({});
try {
response.send(users);
} catch (error) {
response.status(500).send(error);
}
});
module.exports = app;
I would like to refactor the code in the MVC architecture. When I split the code into controller/route configuration, then things do not work.

Google Sheet Api get function returns undefined value in NestJs

I have async function getTables() which using Google Sheet API to get google spreadsheet data. But on response it returns undefined value. this.getClient() returns OAuth2Client data. Could you check my async function, maybe it is written not properly?
async getTables(): Promise<any> {
try {
const sheets = google.sheets({
version: "v4",
auth: await this.getClient()
});
const res = await sheets.spreadsheets.values.get({
spreadsheetId: "sheetId",
range: "A1:B100"
});
return res;
} catch (err) {
throw new HttpException("The API returned an error: " + err, 500);
}
This is getClient async function which authorize client.
async getClient(): Promise<OAuth2Client> {
if (!this.oAuth2Client) {
return await this.authorize();
}
return this.oAuth2Client;
}
private async authorize() {
const credentials = {
access_token: this.auth.access_token,
refresh_token: null
};
this.oAuth2Client = new google.auth.OAuth2(this.CLIENT_ID, this.clientSecret, this.redirectUrl);
this.oAuth2Client.setCredentials(credentials);
return this.oAuth2Client;
}
It was my mistake, I just updated the version of Google API and everything is work, it returns array of data.

Can't send an httpOnly cookie with axios or fetch [duplicate]

Cookies are not sent to the server via getServerSideProps, here is the code in the front-end:
export async function getServerSideProps() {
const res = await axios.get("http://localhost:5000/api/auth", {withCredentials: true});
const data = await res.data;
return { props: { data } }
}
On the server I have a strategy that checks the access JWT token.
export class JwtStrategy extends PassportStrategy(Strategy, "jwt") {
constructor() {
super({
ignoreExpiration: false,
secretOrKey: "secret",
jwtFromRequest: ExtractJwt.fromExtractors([
(request: Request) => {
console.log(request.cookies) // [Object: null prototype] {}
let data = request.cookies['access'];
return data;
}
]),
});
}
async validate(payload: any){
return payload;
}
}
That is, when I send a request via getServerSideProps cookies do not come to the server, although if I send, for example via useEffect, then cookies come normally.
That's because the request inside getServerSideProps doesn't run in the browser - where cookies are automatically sent on every request - but actually gets executed on the server, in a Node.js environment.
This means you need to explicitly pass the cookies to the axios request to send them through.
export async function getServerSideProps({ req }) {
const res = await axios.get("http://localhost:5000/api/auth", {
withCredentials: true,
headers: {
Cookie: req.headers.cookie
}
});
const data = await res.data;
return { props: { data } }
}
The same principle applies to requests made from API routes to external APIs, cookies need to be explicitly passed as well.
export default function handler(req, res) {
const res = await axios.get("http://localhost:5000/api/auth", {
withCredentials: true,
headers: {
Cookie: req.headers.cookie
}
});
const data = await res.data;
res.status(200).json(data)
}

Decorate api requests with Bearer token in NextJS getServerSideProps

I am using axios interceptors to decorate all my requests with an Authorization header.
I get the token from #auth0/nextjs-auth0 by using an API route /api/token:
import auth0 from '#/libs/auth0/auth0';
import { AccessTokenResponse } from '#auth0/nextjs-auth0/dist/tokens/token-cache';
import { NextApiRequest, NextApiResponse } from 'next';
export async function getAccessToken(
req: NextApiRequest,
res: NextApiResponse
): Promise<AccessTokenResponse> {
const tokenCache = auth0(req).tokenCache(req, res);
return tokenCache.getAccessToken();
}
export default async function token(
req: NextApiRequest,
res: NextApiResponse
): Promise<void> {
try {
const { accessToken } = await getAccessToken(req, res);
res.status(200).end(accessToken);
} catch (error) {
res.status(error.status || 500).end(error.message);
}
}
The api route works fine on the client side requests and I can see the token being added and requests going through.
const { token } = await axios.get('/api/token');
const result = {
...config,
headers: {
Authorization: `Bearer ${data}`,
},
};
return result;
However this does not work for the SSR pages when I use the same axios interceptors.
I tried full qualifying the url like so:
const { token } = await axios.get('http://localhost:3000/api/token');
But this throws an error in the SSR layer:
The user does not have a valid session.
I do not know how to get the token from the token cache to be used in the interceptor for the api calls made via SSR?
Any ideas or similar experience?
I opted to store the token as a cookie for all requests and access it through the req key in the context parameter when using SSR:
export const getServerSideProps: GetServerSideProps = async ({ req }) {
const accessToken = req.cookies.token
// Remaining code
}
https://nextjs.org/docs/api-reference/data-fetching/get-server-side-props

Cloud firestore and nextjs - getInitialProps and async data

Im attempting to retrieve a document using the getInitialProps method from nextjs. However it is returning a Promise with a state 'pending'. Here is my code:
static async getInitialProps(context) {
const { id } = context.query;
await db
.collection('clients')
.doc('JJqyDI8a1ILqnqmp2gcO')
.get()
.then(doc => ({
data: doc.data(),
}));
console.log(data); // logs pending
return {
client: data,
};
}
I cant seem to find any examples either.
All async functions return a promise.
I don't see how you're accessing data because when you console.log it is already out of scope.
Do something like this:
static async getInitialProps(context) {
const { id } = context.query;
const data = await db
.collection('clients')
.doc('JJqyDI8a1ILqnqmp2gcO')
.get()
.then(doc => ({
...doc.data(),
}));
return {
client: data,
};
}
This should make it accessible by props.client

Resources