SvelteKit - getSession and handle functions in hook.ts aren't executing - sveltekit

As it already says in the title, the handle and getSession functions in my project don't execute.
Here is my hook.ts file:
import type { GetSession, Handle } from "#sveltejs/kit"
import {parse} from "cookie"
export const handle: Handle = async({event, resolve})=>{
console.log("Handle")
const cookie = await parse(event.request.headers.get("cookies") || "")
event.locals.user.lang = cookie.lang || "sl"
const response = await resolve(event)
return response;
}
export const getSession: GetSession = async(request) => {
return {
user: {
lang: request.locals.user.lang
}
}
}
I don't know if I missed something that's crucial for it to execute, but yeah. I searched a long time for a solution but I did not find anything, even if I followed some tutorials step by step.

Related

How to get session in Next.js middleware? (error in deploy)

import type { NextFetchEvent, NextRequest } from "next/server";
import { getSession } from "next-auth/react";
import { NextResponse } from "next/server";
export async function middleware(req: NextRequest, ev: NextFetchEvent) {
const requestForNextAuth = {
headers: {
cookie: req.headers.get("cookie"),
},
};
//#ts-ignore
const session = await getSession({ req: requestForNextAuth });
if (
req.nextUrl.pathname.startsWith("/fictions/create") &&
(!req.cookies.get("~~session") || !session)
) {
return NextResponse.rewrite(new URL("/enter", req.url));
}
if (
req.nextUrl.pathname.includes("/edit") &&
(!req.cookies.get("~~session") || !session)
) {
return NextResponse.rewrite(new URL("/enter", req.url));
}
if (req.nextUrl.pathname.startsWith("/profile") && !session) {
if (!session) {
return NextResponse.rewrite(new URL("/enter", req.url));
}
}
}
Error Message :
"Dynamic Code Evaluation (e. g. 'eval', 'new Function', 'WebAssembly.compile') not allowed in Edge Runtime
Learn More: https://nextjs.org/docs/messages/edge-dynamic-code-evaluation"
It worked well with local but seems I did something wrong because it seems to cause errors in when deploying project.
I want unauthorized people redirected to '/enter' page by using next-auth session.
So I used getSession.
Is it wrong way to get session in 'edge'?
Then what I should do for?
If I understood well you are trying to check in _middleware.js whether the current user is logged in or not ?
You cannot use getSession() here.
Here is my workaround, it's working in local (didn't try in production yet) :
export async function middleware(req) {
const pathname = req.nextUrl.pathname
const session = await getToken({ req: req, secret: process.env.NEXTAUTH_SECRET }); // I am getting the session here
// Protect protected pages
if (arrayOfProtectedPaths.includes(pathname)) {
if (session === null) {
return NextResponse.redirect("http://localhost:3008/spots/allSpots")
}
}
// Prevent logged in user to access to register and sign in
if (shouldNotBeUser.includes(pathname)) {
if (session !== null) {
return NextResponse.redirect("http://localhost:3008/spots/allSpots")
}
}
}

TypeScript is not recognizing reducer, shows type as any on hover

I am new at Typescript and we are using hooks in our react application. We have a common thunk action creator which calls one of the actions.
appSlice.ts
type ThunkOptions = {
method: number,
api_url: string,
body: any | null
}
/**
* Thunk Action Creator Factory
* #param {String} name : Thunk Action Creator Name
* #returns Thunk Action Creator
*/
export const commonAsyncThunkCreator = (name: string) => {
return createAsyncThunk(name,async (options: ThunkOptions,{dispatch})=>{
let requestHelper;
if(options.method === REQUEST_METHOD.GET.id){
requestHelper = makeGetRequest;
}else if(options.method === REQUEST_METHOD.POST.id){
requestHelper = makePostRequest;
}
const response = await requestHelper(options.api_url,options.body);
if (response.status === 401) {
return dispatch(unauthorized());
}
if(response && response.result){ //TODO: to check
return response;
}
});
}
interface AppReducerInitialStateType {
isFetching: boolean,
isLoggedIn: boolean,
isSyncSuccess: boolean
}
let initialState : AppReducerInitialStateType = {
isFetching:false,
isLoggedIn:true,
isSyncSuccess: false
}
export const appReducer = createSlice({
name:"app",
initialState,
reducers:{
unauthorized:(state)=>{
state.isLoggedIn = false;
state.isSyncSuccess = false;
},
//other reducers
}
});
export const sync = commonAsyncThunkCreator('app/sync'); //a reducer function that i have not provided here but it sets isSyncSuccess flag
export default appReducer.reducer;
export const { unauthorized } = appReducer.actions;
commonAsyncThunkCreator is used in my other reducers also.
In my component files when I access appReducer, it is not recognized.
Note: Please ignore name of key accessed, I have omitted it here in my code snippet
Please tell me where I'm wrong or what approach should I take here
Update: I tried following steps of this post https://blog.logrocket.com/using-typescript-with-redux-toolkit/ but that didn't help either.
Also, I have found that if I do not export unauthorised in appReducer.actions, my appReducer gets recognized as a type but my code breaks since commonAsyncThunkCreator can't work anymore
Since my issue was being caused due to call to unauthorized reducer, I have removed its dependency completely.
export const commonAsyncThunkCreator = (name: string) => {
return createAsyncThunk(name,async (options: ThunkOptions,
{rejectWithValue})=>{
let requestHelper;
if(options.method === REQUEST_METHOD.GET.id){
requestHelper = makeGetRequest;
}else if(options.method === REQUEST_METHOD.POST.id){
requestHelper = makePostRequest;
}
const response = await requestHelper(options.api_url,options.body);
if (response.status === 401) {
return rejectWithValue('You are logged out');
}
if(response && response.result){
return response;
}
});
}
Next, I changed my extraReducers to builder syntax for TS handling and voila, my appReducer is now being recognized.
eg
builder.addCase(logout.pending,(state)=>{
state.isFetching = true;
}),
I am not 100% sure #mk1024, but what about doing something like this :
in your appSlice file you can do
export const appSelector = (state: RootState) => state.app
and then
const { purchaseDetails } = useSelector(appSelector);
Here is some ressource that might be helpful as well
https://blog.logrocket.com/using-typescript-with-redux-toolkit/
Here is an example :
https://github.com/Cvellle/Good-choice-app/blob/696baf99804611a3da27ed87b51f1770280214bd/client/src/app/store.ts ( make sure to add the correct typing in the rootstate )
https://github.com/Cvellle/Good-choice-app/blob/696baf99804611a3da27ed87b51f1770280214bd/client/src/features/login/loginSlice.ts
How to create your slice and export it
How to import and use it
https://github.com/Cvellle/Good-choice-app/blob/696baf99804611a3da27ed87b51f1770280214bd/client/src/features/header/Header.tsx#L22

Nextjs authenticate in a middleware with jwt doesn't work [duplicate]

This question already has answers here:
Nextjs build failing because of jsonwebtoken in _middleware.ts
(3 answers)
Closed 7 months ago.
In this moment I authenticate user in each API. I want to add a middleware to do it only once.
So I created a file named _middleware.ts under /pages/api and used the same approach I did for every API.
I have the following code:
_middleware.ts
import { NextApiRequest } from 'next';
import type { NextFetchEvent, NextRequest } from 'next/server';
import { NextResponse } from 'next/server';
import { getUserIdOrFail } from './utils/jwt';
export async function middleware(req: NextRequest, ev: NextFetchEvent) {
const authToken = req.headers.get('Authorization');
let userId: string;
try {
userId = await getUserIdOrFail({
headers: { authorization: authToken },
} as NextApiRequest);
} catch (err) {
//return 401
}
// here usereId might contain the userId if correctly authenticated
return NextResponse.next();
}
jwt.ts
import { sign, verify } from 'jsonwebtoken';
import { config } from './config';
import { User } from '../models/user';
import { jwtSchema } from './schemas/jwtSchema';
import { NextApiRequest } from 'next';
export const buildJwt = (user: User) =>
sign({ id: user._id }, config.JWT_KEY, { expiresIn: config.JWT_EXPIRATION });
export const verifyJwt = <T>(jwt: string) => {
try {
return verify(jwt, config.JWT_KEY) as T;
} catch (e) {
return null;
}
};
export async function getUserIdOrFail(req: NextApiRequest) {
const authorizationHeader = req.headers.authorization;
if (!authorizationHeader || !authorizationHeader.startsWith('Bearer ')) {
throw new Error();
}
const jwt = authorizationHeader.replace('Bearer ', '');
const parsed = verifyJwt(jwt);
const { value, error } = jwtSchema.validate(parsed);
if (error) {
throw new Error();
}
return value.id as string;
}
Basically with this code I get the bearer token that server gave to the user when he logged in or registered contained into the Authorization header and verify if it is valid. If it is valid I take the userId and use it to do operations with the DB.
The problem is that it does the stuff if I use it in the api files, but in this file it goes into error (catch block with this error). Is it a jsonwebtoken problem? Should I to do it differently? Or should I leave it in the way it actually is?
The problem with this approach is that the jasonwebtoken library uses Dynamic code evaluation which is not allowed into the file _middleware.ts in next.js. To solve this need to use another library or do it in the api files.

Server-side redirects using react HOC when a httpOnly cookie is not present [duplicate]

So I'm creating authentication logic in my Next.js app. I created /api/auth/login page where I handle request and if user's data is good, I'm creating a httpOnly cookie with JWT token and returning some data to frontend. That part works fine but I need some way to protect some pages so only the logged users can access them and I have problem with creating a HOC for that.
The best way I saw is to use getInitialProps but on Next.js site it says that I shouldn't use it anymore, so I thought about using getServerSideProps but that doesn't work either or I'm probably doing something wrong.
This is my HOC code:
(cookie are stored under userToken name)
import React from 'react';
const jwt = require('jsonwebtoken');
const RequireAuthentication = (WrappedComponent) => {
return WrappedComponent;
};
export async function getServerSideProps({req,res}) {
const token = req.cookies.userToken || null;
// no token so i take user to login page
if (!token) {
res.statusCode = 302;
res.setHeader('Location', '/admin/login')
return {props: {}}
} else {
// we have token so i return nothing without changing location
return;
}
}
export default RequireAuthentication;
If you have any other ideas how to handle auth in Next.js with cookies I would be grateful for help because I'm new to the server side rendering react/auth.
You should separate and extract your authentication logic from getServerSideProps into a re-usable higher-order function.
For instance, you could have the following function that would accept another function (your getServerSideProps), and would redirect to your login page if the userToken isn't set.
export function requireAuthentication(gssp) {
return async (context) => {
const { req, res } = context;
const token = req.cookies.userToken;
if (!token) {
// Redirect to login page
return {
redirect: {
destination: '/admin/login',
statusCode: 302
}
};
}
return await gssp(context); // Continue on to call `getServerSideProps` logic
}
}
You would then use it in your page by wrapping the getServerSideProps function.
// pages/index.js (or some other page)
export const getServerSideProps = requireAuthentication(context => {
// Your normal `getServerSideProps` code here
})
Based on Julio's answer, I made it work for iron-session:
import { GetServerSidePropsContext } from 'next'
import { withSessionSsr } from '#/utils/index'
export const withAuth = (gssp: any) => {
return async (context: GetServerSidePropsContext) => {
const { req } = context
const user = req.session.user
if (!user) {
return {
redirect: {
destination: '/',
statusCode: 302,
},
}
}
return await gssp(context)
}
}
export const withAuthSsr = (handler: any) => withSessionSsr(withAuth(handler))
And then I use it like:
export const getServerSideProps = withAuthSsr((context: GetServerSidePropsContext) => {
return {
props: {},
}
})
My withSessionSsr function looks like:
import { GetServerSidePropsContext, GetServerSidePropsResult, NextApiHandler } from 'next'
import { withIronSessionApiRoute, withIronSessionSsr } from 'iron-session/next'
import { IronSessionOptions } from 'iron-session'
const IRON_OPTIONS: IronSessionOptions = {
cookieName: process.env.IRON_COOKIE_NAME,
password: process.env.IRON_PASSWORD,
ttl: 60 * 2,
}
function withSessionRoute(handler: NextApiHandler) {
return withIronSessionApiRoute(handler, IRON_OPTIONS)
}
// Theses types are compatible with InferGetStaticPropsType https://nextjs.org/docs/basic-features/data-fetching#typescript-use-getstaticprops
function withSessionSsr<P extends { [key: string]: unknown } = { [key: string]: unknown }>(
handler: (
context: GetServerSidePropsContext
) => GetServerSidePropsResult<P> | Promise<GetServerSidePropsResult<P>>
) {
return withIronSessionSsr(handler, IRON_OPTIONS)
}
export { withSessionRoute, withSessionSsr }

AWS GET request returning network error when called from client

I have a GET function elseware in the project to list all items in my dynamodb, which works perfectly, however when i use it again but target an item with a specific id, i get this network error. I have ran the function in lamdba directly and it works so im a bit confused.
Here is the call:
import React, { useRef, useState, useEffect } from "react";
import { API } from "aws-amplify";
export default function Sites(props) {
const file = useRef(null);
const [site, setSite] = useState(null);
const [content, setContent] = useState("");
useEffect(() => {
function loadSite() {
return API.get("sites", `/sites/${props.match.params.id}`);
}
async function onLoad() {
try {
const site = await loadSite();
const { content } = site;
console.log(content);
setContent(content);
setSite(site);
} catch (e) {
alert(e);
}
}
onLoad();
}, [props.match.params.id]);
return (
<div className="Sites"></div>
);
}
EDIT:
Also here is the route from the listall items page:
<AppliedRoute path="/sites/:id" exact component={Sites} props={childProps} />
Method Code:
import * as dynamoDbLib from "./libs/dynamodb-lib";
import { success, failure } from "./libs/response-lib";
export async function main(event, context) {
const params = {
TableName: "sites",
// 'KeyConditionExpression' defines the condition for the query
// - 'userId = :userId': only return items with matching 'userId'
// partition key
// 'ExpressionAttributeValues' defines the value in the condition
// - ':userId': defines 'userId' to be Identity Pool identity id
// of the authenticated user
KeyConditionExpression: "userId = :userId",
ExpressionAttributeValues: {
":userId": event.requestContext.identity.cognitoIdentityId
}
};
try {
const result = await dynamoDbLib.call("query", params);
// Return the matching list of items in response body
return success(result.Items);
} catch (e) {
console.log(e);
return failure({ status: false });
}
}
Also from testing it in AWS API Gateway I receive a 500 error with the following logs:
https://pastebin.com/FHQnZAqt
My code was missing the implementation for the API GET /user/{id} route and that caused the 500 response.

Resources