How can I prevent third-party cookies from being set on my site? - express

I just noticed that some third-party tracking cookies have been set on my website without notifying me.
<img src="https://i.pravatar.cc/64" />
Rendering this creates 2 google analytics cookies on my site.
https://pravatar.cc/
They have no notices about setting cookies or tracking sites when using their API.
This is my server setup:
declare module 'express-session' {
export interface SessionData {
user: {
id: string;
organisation: string;
};
}
}
const startApolloServer = async () => {
const RedisStore = connectRedis(session);
const redisClient = createClient({ legacyMode: true });
redisClient.connect().catch(console.error);
const app = express();
//Redis
app.use(
session({
name: 'uId',
store: new RedisStore({ client: redisClient, disableTouch: true }),
saveUninitialized: false,
secret: 'mysecret',
resave: false,
cookie: {
maxAge: 1000 * 60 * 60 * 24, //One day
secure: false, //dev only
httpOnly: false, //dev only
sameSite: 'lax',
},
}),
);
const server = new ApolloServer({
schema,
context: ({ req, res }: { req: Request; res: Response }) => ({
req,
res,
}),
});
await server.start();
server.applyMiddleware({
app,
cors: {
credentials: true,
origin: ['http://localhost:3000', 'http://localhost:4000'],
},
bodyParserConfig: true,
});
await new Promise((resolve) =>
app.listen({ port: 4000 }, resolve as () => void),
);
console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`);
return { server, app };
};
startApolloServer();
How can I allow cookies to be set only from my http api (Later my https api)?

Related

Why can I not set/see the cookie in my browser dev tools?

I am following Ben Awads Reddit Clone Tutorial and have a problem with setting a cookie.
I have an express app and I am trying to set a cookie if the user provides a correct username and password to my GraphQL mutation. When I use Apollo GraphQLs Sandbox to send a login request. I can see in the Network Tab, that there is a Set-Cookie Header with the correct data. But when I send a request to see if there is another query and check for req.session.userId it is undefined.
Here is my index.ts file:
import 'reflect-metadata';
import express from 'express';
import { ApolloServer } from 'apollo-server-express';
import { PrismaClient } from '#prisma/client';
import { buildSchema } from 'type-graphql';
import { PostResolver } from './resolvers/post';
import { UserResolver } from './resolvers/user';
import { __prod__ } from './constants';
import { MyContext } from './types';
import session from 'express-session';
import connectRedis from 'connect-redis';
import { createClient } from 'redis';
const prisma = new PrismaClient();
const main = async () => {
const app = express();
const RedisStore = connectRedis(session);
const redisClient = createClient({ legacyMode: true });
redisClient.connect().catch(console.error);
app.use(
session({
name: 'qid',
cookie: {
maxAge: 1000 * 60 * 60 * 24 * 365 * 10, // 10 years
httpOnly: true,
sameSite: 'lax', // lax für csrf
secure: __prod__, // cookie only works in https
},
secret: 'keyboard cat',
store: new RedisStore({ client: redisClient }),
saveUninitialized: false,
resave: false,
})
);
const apolloServer = new ApolloServer({
schema: await buildSchema({
resolvers: [PostResolver, UserResolver],
validate: false,
}),
context: ({ req, res }): MyContext => ({ prisma, req, res }),
});
await apolloServer.start();
apolloServer.applyMiddleware({ app });
app.listen(4000, () => {
console.log('Server is running on http://localhost:4000');
});
};
And here is my users resolver:
#Resolver()
export class UserResolver {
// create a me query
#Query(() => UserType, { nullable: true })
async me(#Ctx() { prisma, req }: MyContext) {
// you are not logged in
if (!req.session.userId) {
return null;
}
const user = await prisma.user.findUnique({
where: {
id: req.session.userId,
},
});
return user;
}
#Mutation(() => UserResponse)
async login(
#Arg('options') options: UsernamePasswordInput,
#Ctx() { prisma, req }: MyContext
): Promise<UserResponse> {
const user = await prisma.user.findUnique({
where: {
username: options.username,
},
});
if (!user) {
return {
errors: [
{
field: 'username',
message: 'That username does not exist',
},
],
};
}
if (!(await argon2.verify(user.password, options.password))) {
return {
errors: [
{
field: 'password',
message: 'Incorrect password',
},
],
};
}
req.session.userId = user.id;
return {
user,
};
}
}
I tried using different versions of the dependencies "connect-redis", "redis" and "express-session"

Express [Nest.js] - res.cookie() not set in Heroku production

I am using Next.js (Frontend) and Nest.js (Backend) Frameworks and I am having issues with a HTTP Cookies. The following code works in development trough localhost, but not when publishing the code to vercel (Next.js) and Heroku (Nest.js)
Here is a snippet of my Nest.js Setup
// main.ts
async function bootstrap() {
const app = await NestFactory.create(AppModule, {
logger: new LoggerService(),
cors: true,
});
app.use(cookieParser());
app.enableCors({
origin: ["<my-site>"],
credentials: true,
});
...
await app.listen(process.env.PORT || 5000);
}
bootstrap();
Here is my Route:
#Post("login/verify")
#HttpCode(201)
#ApiResponse({ description: "returns JWT for verified login attempt" })
async verify(
#Body() method: VerificationDto,
#Req() req: Request,
#Res({ passthrough: true }) res: Response
) {
const { accessToken } = await this.userService.verifyLogin(method);
const origin = req.get("origin");
this.loggerService.log(`origin: ${origin}`);
res
.set("Access-Control-Allow-Credentials", "true")
.set("Access-Control-Allow-Origin", origin)
.cookie("accessToken", accessToken, {
expires: new Date(new Date().getTime() + 60 * 1000 * 60 * 2),
sameSite: "none",
secure: true,
httpOnly: false,
});
return {
accessToken,
refreshToken,
};
}
Here is my useAuth component in Next.js
import React from "react";
import jwtDecode from "jwt-decode";
import nookies from "nookies";
import { redirect } from "../../services/redirect";
import { GetServerSideProps, NextPage } from "next";
export interface User {
accessToken: string;
email: string;
exp: number;
iat: number;
id: string;
name: string;
roles: Array<string>;
}
const AuthContext = React.createContext<User>(null as unknown as User);
const loginRoute = `/login`;
export const authenticate = (
getServerSidePropsInner: GetServerSideProps = async () => ({ props: {} })
) => {
const getServerSideProps: GetServerSideProps = async ctx => {
const { req, res } = ctx;
if (!req.headers.cookie) {
console.log("no cookie found");
redirect(ctx, loginRoute);
return { props: {} };
}
const { accessToken } = nookies.get(ctx);
console.log(accessToken);
let user = null;
try {
user = {
accessToken,
...(jwtDecode(accessToken as string) as object),
};
} catch (e) {
console.log(e);
redirect(ctx, loginRoute);
}
const result = await getServerSidePropsInner(ctx);
return {
...result,
props: {
user,
//#ts-ignore
...result.props,
},
};
};
return getServerSideProps;
};
export const withAuth = (C: NextPage) => {
const WithAuth = (props: any) => {
const { user, ...appProps } = props;
return (
<AuthContext.Provider value={user}>
<C {...appProps} />
</AuthContext.Provider>
);
};
WithAuth.displayName = `WithAuth(${C.displayName})`;
return WithAuth;
};
export default withAuth;
export const useAuth = (): User => React.useContext(AuthContext);
I am using axios with the following login request
verifyLogin: ({ code, email }: { code: string; email: string }) => {
return axios(`${apiURI}/user/login/verify`, {
method: `POST`,
withCredentials: true,
headers: {
"Content-Type": "application/json",
},
data: {
code: code,
email: email,
},
});
},
Setup works with localhost. It adds a http cookie which is read by the next.js application.
However, when deploying my Nest.js app to Heroku, the cookie does not seem to send to the next application. I have tried adding different values for the CookieOptions
sameSite: "none",
secure: true,
httpOnly: false,
However, this did not help either.
Any help is much appreciated as I am fighting with this for days.

session based authentication in next.js with express-sessions

hello i am trying to implement session based authentication for my next.js custom server express app .when i login session is set and it work as the way i expected but after few seconds or few request it don't. when i console.log(req.session) session is present but req.session.isLoggedin is disappeared.I want to implement session base authentication with database.
////server.js
/SOME NEXT.JS CUSTOM SERVER CODE/
app.prepare().then(() => {
const session = require('express-session');
const bodyParser = require('body-parser');
const Mongodbstore = require('connect-mongodb-session')(session);
const mongodburl = 'MONGODB_URL';
const server = express();
const store = new Mongodbstore({
uri: mongodburl,
collection: 'sessions',
});
server.use(bodyParser.urlencoded({ extended: false }));
server.use(
session({
secret: 'VggyuvyhfSvcj',
name: 'sessionID',
resave: false,
saveUninitialized: false,
cookie: {
maxAge: 100000,
sameSite: 'strict',
secure: false,
},
store: store,
})
);
server.post('/', (req, res, next) => {
const { user, password } = req.body;
if (user === 'hello') {
req.session.isLoggedin = true;
return res.redirect('/home');
}
return res.redirect('/');
});
server.post('/logout', (req, res, next) => {
req.session.destroy((err) => {
if (err) {
console.log(err);
return res.redirect('/home');
}
res.clearCookie('sessionID');
return res.redirect('/');
});
});
server.all('*', (req, res) => {
return handle(req, res);
});
/SOME NEXT.JS CUSTOM SERVER CODE/
//// page/home.js
export default function Home() {
return (
<>
<Logo />
<Nav />
<div className="body">
HOME
</div>
<Foot />
</>
);
}
export async function getServerSideProps(contex) {
const { req, res } = contex;
console.log(req.session);
if (!req.session.isLoggedin) {
return { redirect: { destination: '/', permanent: false } };
}
return {
props: {
user: 'USER1',
},
};
}
console.log
Session {
cookie: {
path: '/',
_expires: 2021-05-15T05:03:12.921Z,
originalMaxAge: 100000,
httpOnly: true,
secure: false,
domain: null,
sameSite: 'strict'
},
isLoggedin: true
}
after few seconds or request
Session {
cookie: {
path: '/',
_expires: 2021-05-15T05:03:27.188Z,
originalMaxAge: 100000,
httpOnly: true,
sameSite: 'strict',
secure: false
}
}
i don't know why isLoggedin is disappeared after few request. but session is present in database and cookie in browser.

cant get current User from API with axios

i have a problem to get my auth User. When I call my route where the current user is in i become no Data in my Vue file.
Backend:
var app = express();
app.use(cors({
methods:['GET','POST'],
credentials: true,
}))
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser('test'))
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(session({
secret: 'test',
resave: true,
saveUninitialized: true,
}))
app.use(passport.initialize());
app.use(passport.session());
const port = 3000
app.use(express.static('public'))
//Passport setup
const apiRoutes = require('./apiRoutes')
app.use('/api', apiRoutes)
Passport config:
passport.use(new LocalStrategy(
{
usernameField: 'username',
passwordField: 'password'
},
function(username, password, done) {
userModel.findOne({
username: username
}, (req, res) => {
const match = bcrypt.compare(password, res.password).then(result => {
if (result) {
return done(null, res, {message: 'hat geklappt'})
} else {
return done(null, false, {message: 'hat nicht geklappt'})
}
})
})
}
));
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
Route:
router.get("/", (req, res) => {
let user = req.user;
res.json(
user
);
});
My API is not empty but i cant get the data from Axios get
API:
APi
and my Empty data field:
vue
i dont know what i can do because i have not so much experience and iam really thankfull for help
Thanks :)
set content-type: application/json in the header like this:
it's a sample
var axios = require('axios');
var data = JSON.stringify({"username":"username","password":"password"});
let domain = "localhost";
let port = "3000"
let url = `http://${local}:${port}/api`
var config = {
method: 'post',
url: '',
headers: {
'Content-Type': 'application/json'
},
data : data
};
axios(config)
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});
the request is not added to the question, but you can change your request base on my answer

passport.js + express + apollo-server, req.user is undefined

I tried to record sessions in the database through sequelize, to make it serverless, but req.user is always undefined, I tried every manual which I found on the internet, I do not understand why it not working.
I tried passport.js manual, express manuals, github gists.
Records in the database are successfully created, on successful authentication, but when I try hit /graphql endpoint, it does not fill req.user with user.
req.user should be restored based on session hash which is stored in database.
#!/usr/bin/env node
import express from 'express';
import session from 'express-session';
import { ApolloServer } from 'apollo-server-express';
import { typeDefs, resolvers } from './graphql';
import orm from './orm';
import compose from './dataloader/status.dataloader';
import passport from 'passport';
import { Strategy as GitHubStrategy } from 'passport-github';
import cors from 'cors';
const app = express();
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => {
const user = req.user;
console.log({ user });
return {
user,
orm,
dataloader: compose(orm),
};
},
});
passport.use(
new GitHubStrategy(
{
clientID: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
callbackURL: `/auth/github/callback`,
},
async (accessToken, refreshToken, profile, done) => {
const { provider, id: externalId, profileUrl, username, displayName, photos } = profile;
const photo = photos && photos[0] && photos[0].value;
const user = await orm.User.findOne({
include: [
{
attributes: [],
model: orm.UserProvider,
where: {
provider,
externalId,
},
},
],
raw: true,
}).then(async (v) => {
if (null !== v) {
return v;
}
v = await orm.User.create({
displayName,
photo,
});
await orm.UserProvider.create({
provider,
internalId: v.id,
externalId,
username,
profileUrl,
});
return v;
})
const session = await orm.UserSession.create({
internalId: user.id,
hash: accessToken,
});
return done(null, session);
}
)
);
passport.serializeUser(({ hash }, done) => {
console.log({ hash });
done(null, hash);
});
passport.deserializeUser(async (hash, done) => {
console.log({ hash });
const user = await orm.User.findOne({
include: [
{
attributes: [],
model: orm.UserSession,
where: {
hash,
},
},
],
raw: true,
});
done(null, user);
});
app.use(
cors({
origin: "*",
methods: "GET,POST",
preflightContinue: false,
optionsSuccessStatus: 204,
credentials: true,
})
);
app.use(session({ secret: 'test' }));
app.use(passport.initialize());
app.use(passport.session());
app.get(
'/auth/github',
passport.authenticate('github', { session: true })
);
app.get(
'/auth/github/callback',
passport.authenticate('github', { session: true }),
(req, res) => res.redirect('/')
);
app.use('/graphql', passport.authenticate('session', { session: true }));
// (req, res, next) => {
// debugger;
// // passport.
// console.log({
// req,
// session: JSON.stringify(req.session, ',', 4),
// cookie: JSON.stringify(req.cookie),
// user: req.user,
// });
// return next();
// });
server.applyMiddleware({ app, path: '/graphql' });
app
.listen(process.env.PORT, () => {
console.log(`GraphQL ready on: http://localhost:${process.env.PORT}/graphql`);
});