How to access pages with basic authentication (Apify SDK) - authentication

In the puppeteer documentation i found that i could use
await page.authenticate({ username: 'test', password: 'test' });
To access pages with basic authentication.
But it seems that the handlePageFunction has already done the request.
So how could i do that?
Apify.main(async () => {
const requestQueue = await Apify.openRequestQueue(`PC_${settings.project}_${time}`);
await requestQueue.addRequest({ url: settings.baseUrl });
const crawler = new Apify.PuppeteerCrawler({
requestQueue,
launchPuppeteerOptions: {
headless: settings.headless,
// slowMo: 500,
},
maxRequestsPerCrawl: settings.maxurls,
maxConcurrency: settings.maxcrawlers,
handlePageFunction: async ({ request, response, page }) => {
await page.authenticate({ username: 'test', password: 'test' });
await page.waitFor(settings.waitForPageload);
const requestUrl = request.url
const loadUrl = request.loadedUrl
let isRedirected = false
if (requestUrl !== loadUrl) {
isRedirected = { from: requestUrl, to: loadUrl }
}

You can manipulate the page before it is opened with gotoFunction.
If you would need to login to a website, you can check this small login example
const crawler = new Apify.PuppeteerCrawler({
gotoFunction: async ({ page, request }) => {
await page.authenticate({ username: 'test', password: 'test' });
return page.goto(request.url, { timeout: 120000 });
},

Related

authorization header undefined, empty

Can you explain what I'm doing wrong, I don't understand why my header is empty. Login is working but when I trigger the me query it's not working, not authenticated appear and if I console log this it's undefined.
How to keep the user logged, and be able to acces the data ?
Here is my code, if you know what's wrong.
Thanks
import * as dotenv from 'dotenv';
dotenv.config();
import express from 'express';
import { ApolloServer } from 'apollo-server-express';
import { PrismaClient } from '#prisma/client';
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
const JWT_SECRET = 'abc123';
const typeDefs = `
type User {
id: ID
username: String
}
type Query {
me: User
}
type Mutation {
signup(username: String!, password: String!): User
login(username: String!, password: String!): User
}
`;
const resolvers = {
Query: {
async me(_, __, context) {
// Check if the request has a valid JWT
const auth = context.req.headers.authorization;
if (!auth) {
throw new Error('Not authenticated');
}
// Get the user ID from the JWT
const token = auth.split('Bearer ')[1];
const { userId } = jwt.verify(token, JWT_SECRET);
// Retrieve the user from the database
return context.prisma.user({ id: userId });
},
},
Mutation: {
async signup(_, { username, password }, context) {
// Hash the password
const hashedPassword = await bcrypt.hash(password, 10);
// Create a new user in the database
const user = await context.prisma.user.create({
data: {
username,
password: hashedPassword,
},
});
// Create a JWT for the new user
const token = jwt.sign({ userId: user.id }, JWT_SECRET);
return { token, ...user };
},
async login(_, { username, password }, context) {
// Retrieve the user from the database
const user = await context.prisma.user.findUnique({
where: {
username: username,
},
});
if (!user) {
throw new Error('Invalid login');
}
// Compare the provided password with the hashed password
const valid = await bcrypt.compare(password, user.password);
if (!valid) {
throw new Error('Invalid login');
}
// Create a JWT for the user
const token = jwt.sign({ userId: user.id }, JWT_SECRET);
return { ...user, token };
},
},
};
const main = async () => {
const app = express();
const prisma = new PrismaClient();
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => ({ req, prisma }),
});
await server.start();
server.applyMiddleware({ app });
app.listen(4000, () =>
console.log(`GraphQL server running on http://localhost:4000`)
);
};
main().catch((err) => {
console.error(err);
});

how to delete cookie without reloading the page using custom express server in next js

this is my routes file
const User = require("./schema");
const jwt = require("jsonwebtoken");
const bcrypt = require("bcrypt");
const express = require("express");
const server2 = express();
const routes = (server,app) => {
// this route is for signup
server.post("/api/signup", async (req,res) => {
const { fullName, emailId, password }=req.body;
const signupData = new User({
fullName,
emailId,
password: await bcrypt.hash(password,10),
});
const token = await signupData.generateToken();
console.log(`token: ${token}`);
if (token != undefined) {
res.cookie("token",token,{
httpOnly: true,
secure: true,
sameSite: "strict",
path: "/",
maxAge: 1000*60*2
})
res.send({message:"signedup successfuly"});
res.end();
}
const saveUser = await signupData.save();
});
// this route is for user varification
server.get("/api", async (req,res) => {
const token = req.cookies.token;
console.log(`token: ${token}`);
if (token != undefined) {
const _id = jwt.verify(token,process.env.JWT_SECRET)._id;
const findUser = await User.findOne({ _id });
res.send(findUser);
} else {
res.send({fullName:""});
}
});
// this route is for logout
server.get("/api/logout",async (req, res) => {
try {
const token = req.cookies.token;
const _id = jwt.verify(token, process.env.JWT_SECRET)._id;
await User.updateOne({ _id }, { $pull: { tokens: { token }}});
res.clearCookie("token");
console.log(req.cookies.token)
res.send({message:""})
res.end();
} catch(err) {
console.log(err)
}
});
}
module.exports = routes;
When I do signup cookie is setting without reloading the page its working but that's not a problem the problem is when I do logout its not deleting cookie without reloading the page when I reload the page its working but without reloading the page its not working I am using custom express server in nextjs

Testing authentication with Auth0 in a full stack application with Cypress

I’m working on a full-stack NestJS application, integrating with Auth0 using the express-openid-connect library. I’m using Cypress for e2e tests, and I’m trying to find a way of testing my login using Cypress.
I found this article - https://auth0.com/blog/end-to-end-testing-with-cypress-and-auth0/, but it seems to be very much tied to a React application. I’m calling the /oauth/token API endpoint, and I get a response, but I’m unsure how to build out my callback URL to log me in to the application. Here’s what I have so far:
Cypress.Commands.add('login', () => {
cy.session('logged in user', () => {
const options = {
method: 'POST',
url: `${Cypress.env('OAUTH_DOMAIN')}/oauth/token`,
body: {
grant_type: 'password',
username: Cypress.env('AUTH_USERNAME'),
password: Cypress.env('AUTH_PASSWORD'),
scope: 'openid profile email',
audience: `${Cypress.env('OAUTH_DOMAIN')}/api/v2/`,
client_id: Cypress.env('OAUTH_CLIENT_ID'),
client_secret: Cypress.env('OAUTH_CLIENT_SECRET'),
},
};
cy.request(options).then((response) => {
// What do I do here?
});
});
});
Any pointers would be gratefully recieved!
I ended up sorting this out by using Puppeteer to handle my login, stopping at the point of redirection to the callback URL and returning the cookies and callback URL to Cypress, as detailed in this article:
https://sandrino.dev/blog/writing-cypress-e2e-tests-with-auth0
Things have changed a bit since then, and with the introduction of Cypress's experimentalSessionSupport it's a bit simpler. I ended up whittling the solution down to having the following in my Cypress setup:
// cypress/plugins/auth0.js
const puppeteer = require('puppeteer');
const preventApplicationRedirect = function (callbackUrl) {
return (request) => {
const url = request.url();
if (request.isNavigationRequest() && url.indexOf(callbackUrl) === 0)
request.respond({ body: url, status: 200 });
else request.continue();
};
};
const writeUsername = async function writeUsername({ page, options } = {}) {
await page.waitForSelector('#username');
await page.type('#username', options.username);
};
const writePassword = async function writeUsername({ page, options } = {}) {
await page.waitForSelector('#password', { visible: true });
await page.type('#password', options.password);
};
const clickLogin = async function ({ page } = {}) {
await page.waitForSelector('button[type="submit"]', {
visible: true,
timeout: 5000,
});
const [response] = await Promise.all([
page.waitForNavigation({ waitUntil: 'networkidle2' }),
page.click('button[type="submit"]'),
]);
return response;
};
exports.Login = async function (options = {}) {
const browser = await puppeteer.launch({
headless: options.headless,
args: options.args || ['--no-sandbox', '--disable-setuid-sandbox'],
});
const page = await browser.newPage();
try {
await page.setViewport({ width: 1280, height: 800 });
await page.setRequestInterception(true);
page.on('request', preventApplicationRedirect(options.callbackUrl));
await page.goto(options.loginUrl);
await writeUsername({ page, options });
await writePassword({ page, options });
const response = await clickLogin({ page, options });
if (response.status() >= 400) {
throw new Error(
`'Login with user ${
options.username
} failed, error ${response.status()}`,
);
}
const url = response.url();
if (url.indexOf(options.callbackUrl) !== 0) {
throw new Error(`User was redirected to unexpected location: ${url}`);
}
const { cookies } = await page._client.send('Network.getAllCookies', {});
return {
callbackUrl: url,
cookies,
};
} finally {
await page.close();
await browser.close();
}
};
// cypress/plugins/index.js
const auth0 = require('./auth0');
module.exports = (on, config) => {
require('dotenv').config({ path: '.env.test' });
config.env.AUTH0_DOMAIN = process.env.AUTH0_DOMAIN;
config.env.AUTH_USERNAME = process.env.AUTH_USERNAME;
config.env.AUTH_PASSWORD = process.env.AUTH_PASSWORD;
on('task', {
LoginPuppeteer(options) {
return auth0.Login(options);
},
});
return config;
};
// cypress/support/commands.js
const { getUnixTime } = require('date-fns');
/*
* Create the cookie expiration.
*/
function getFutureTime(minutesInFuture) {
const time = new Date(new Date().getTime() + minutesInFuture * 60000);
return getUnixTime(time);
}
/**
* Create a cookie object.
* #param {*} cookie
*/
function createCookie(cookie) {
return {
name: cookie.name,
value: cookie.value,
options: {
domain: `${cookie.domain.trimLeft('.')}`,
expiry: getFutureTime(15),
httpOnly: cookie.httpOnly,
path: cookie.path,
sameSite: cookie.sameSite,
secure: cookie.secure,
session: cookie.session,
},
};
}
/**
* Login via puppeteer and return the redirect url and cookies.
*/
function login() {
return cy.task('LoginPuppeteer', {
username: Cypress.env('AUTH_USERNAME'),
password: Cypress.env('AUTH_PASSWORD'),
loginUrl: 'http://localhost:3000/login',
callbackUrl: 'http://localhost:3000/callback',
});
}
/**
* Login with Auth0.
*/
Cypress.Commands.add('loginAuth0', () => {
cy.session('logged in user', () => {
login().then(({ cookies, callbackUrl }) => {
console.log(cookies);
cookies
.map(createCookie)
.forEach((c) => cy.setCookie(c.name, c.value, c.options));
cy.visit(callbackUrl);
});
});
});
You can then use cy.loginAuth0() in your app to login with a real Auth0 instance. Make sure you have "experimentalSessionSupport": true in your cypress.json. That way you'll only have to perform this (admittedly long winded) task only once in your test suite!

onRemoteStreamListener doesn't get triggered on the Initiator's Side (Web App)

I am trying to build a video chat application between 2 users using the sdk provided by Connecty Cube everything works fine so far till.
User 1 (caller) : initiates the call
User 2 (opponent) : receives the call and accepts it
User 1 & User 2 : should get a call back function onRemoteStreamListener in order to start the Video Session between them
What actually happens User 2 only gets the Event for OnRemoteStreamListener and could view/hear User 1, while onRemoteStreamListener doesn't get triggered in User 1 side. I am not sure what is the problem as I have been going through the documentation provided by Connecty Cube and having it as my reference for the integration
link: https://developers.connectycube.com/js/videocalling?id=accept-a-call
ConnectyCube SDK Version : 3.9.1
Here are samples for the code:
User 1 (Caller Code)
async startVideoSession() {
try {
const {
dispatch,
getters: {
getConnectyCubeSessionInfo,
getUserData,
getSelectedVideoAppointement
}
} = this.$store;
const patientConnectyCubeUserResponse = await dispatch(
"getUserFromConnectyCube",
{
sessionInfo: getConnectyCubeSessionInfo,
email: getSelectedVideoAppointement.patient.email
}
);
const doctorConnectyCubeUserResponse = await dispatch(
"getUserFromConnectyCube",
{
sessionInfo: getConnectyCubeSessionInfo,
email: getUserData.email
}
);
const {
Credentials: { appId }
} = connectyCubeClient;
const client = new ConnectyCube();
await client.init({ appId, token: getConnectyCubeSessionInfo.token });
await client.createSession({
login: ########,
password: ##########
});
const token = client.service.sdkInstance.session.token;
await client.chat.connect({
userId: doctorConnectyCubeUserResponse.user.id,
password: token
});
const isConnected = await client.chat.isConnected;
if (isConnected) {
const calleesIds = [patientConnectyCubeUserResponse.user.id]; // User's ids
const sessionType = client.videochat.CallType.VIDEO; // AUDIO is also possible
const additionalOptions = { bandwidth: 256 };
const session = await client.videochat.createNewSession(
calleesIds,
sessionType,
additionalOptions
);
const mediaParams = {
audio: true,
video: true,
options: {
muted: true,
mirror: true
}
};
await session
.getUserMedia(mediaParams)
.then(localStream => {
session.attachMediaStream("doctor-video", localStream);
const extension = {};
session.call(extension, error => {
console.log(error);
});
})
.catch(err => {
console.error(err);
});
console.log("last console.log");
client.videochat.onAcceptCallListener = this.onAcceptCallListener;
// client.videochat.onUserNotAnswerListener = function(session, userId) {
// console.log("call refused");
// };
client.videochat.onSessionConnectionStateChangedListener = function(
session,
userID,
connectionState
) {
console.log("Connection state => \n", connectionState);
};
client.videochat.onRemoteStreamListener = this.OnRemoteStreamListener;
}
} catch (err) {
console.log("ERRRRRROR", err);
}
}
OnRemoteStreamListener(session, userId, remoteStream) {
// attach the remote stream to DOM element
console.log("STREAM FROM DOCTOR");
session.attachMediaStream("patient-video", remoteStream);
},
User 2 (opponent code)
async initalizeConnectyCube() {
const {
getters: { getPatientData }
} = this.$store;
const client = new ConnectyCube();
const { Credentials, Config } = connectyCubeClient;
await client.init(Credentials, Config);
const sessionInfo = await client.createSession();
await client.init({ appId: Credentials.appId, token: sessionInfo.token });
const sessionStatus = await client.createSession({
login: ########,
password: ########
});
const token = client.service.sdkInstance.session.token;
await client.chat.connect({
userId: sessionStatus.user.id,
password: token
});
const isUserConnected = await client.chat.isConnected;
if (isUserConnected) {
client.videochat.onCallListener = this.handleOnCallListener;
client.videochat.onRemoteStreamListener = this.handleOnRemoteStreamListener;
}
}
handleOnRemoteStreamListener(session, userID, remoteStream) {
// attach the remote stream to DOM element
console.log("STREAM FROM CALLER");
session.attachMediaStream("patient-video", remoteStream);
}

setting cookie after google passport callback

I am using facebook and google oauth2 login using passport js, with this flow
User clicked the login button
Redirects to facebook/google auth page (depending on what login the user chooses)
The auth page redirects back to a callback page (/auth/callback/[provider])
A passport express middleware will catch it to parse some data and then send it to a remote api of myown to sign the user in
The auth remote api will send a response back consisting the user token
A custom express middleware will catch the response to set cookie on the server
the express chain ends by route it to /profile (cookie with token is set on the browser)
/profile will then checks if there is a token, if there is not: it will redirect to /
Doing this flow on facebook login is fine, the user is successfully redirected to /profile, with all of its data and token, the google oauth2 login however seems to be doing the redirect to /profile then setting the token (step #7 then #6), so everytime the user is using google oauth2 login, its always gonna be redirected back to / since by the time it arrives at /profile, it doesnt have the token
here's the code on the above's flow
#./server.js
const express = require('express')
const next = require('next')
const Passport = require('./server/middleware/passport')
const Api = require('./server/api')
const port = parseInt(process.env.PORT, 10)
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
app
.prepare()
.then(() => {
const server = express()
// ... other unrelated things
server.use(Passport.initialize())
Api.passport.facebook(server)
Api.passport.facebookCallback(server)
Api.passport.google(server)
Api.passport.googleCallback(server)
// ... other unrelated things
server.all('*', (req, res) => handle(req, res))
server.listen(port, (error) => {
if (error) throw error
// ... other unrelated things
})
})
#./server/api.js
const Passport = require('middleware/passport')
function setCookie(req, res, next) {
res.cookie('token', req.user.auth.token, {
httpOnly: true,
sameSite: 'strict',
path: '/',
secure: process.env.NODE_ENV !== 'development',
})
next()
}
function facebook(app) {
return app.get('/auth/facebook', (req, res, next) => {
Passport.authenticate('facebook', {
scope: ['email', 'public_profile']
})(req, res, next)
})
}
function facebookCallback(app) {
return app.get(
'/auth/callback/facebook',
Passport.authenticate('facebook', { session: false, failureRedirect: '/' }),
setCookie,
(req, res) => {
res.redirect('/profile')
},
)
}
function google(app) {
return app.get('/auth/google', (req, res, next) => {
Passport.authenticate('google', {
scope: [
'https://www.googleapis.com/auth/userinfo.email ',
'https://www.googleapis.com/auth/userinfo.profile ',
],
prompt: 'consent',
authType: 'rerequest',
accessType: 'offline',
})(req, res, next)
})
}
function googleCallback(app) {
return app.get(
'/auth/callback/google',
Passport.authenticate('google', { failureRedirect: '/', session: false }),
setCookie,
(req, res) => {
res.redirect('/profile')
},
)
}
module.exports = {
passport: {
facebook,
facebookCallback,
google,
googleCallback,
}
}
#./server/middleware/passport.js
const axios = require('axios')
const passport = require('passport')
const GoogleStrategy = require('passport-google-oauth20').Strategy
const FacebookStrategy = require('passport-facebook').Strategy
passport.serializeUser((user, done) => {
done(null, user)
})
passport.deserializeUser((obj, done) => {
done(null, obj)
})
function verifyCallback(req, ... , done) {
process.nextTick(async () => {
try {
const options = {
baseURL: baseUrl, // My remote api url
method: 'POST',
url: '/auth/signin',
headers: {
'Content-Type': 'application/json',
},
data: JSON.stringify({
// email, fullname, etc
}),
}
const response = await axios(options)
return done(null, response.data)
} catch (error) {
const { response } = error
return done(JSON.stringify(response.data, null, 2), null)
}
})
}
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: callbackURLGoogle,
passReqToCallback: true,
}, verifyCallback))
passport.use(new FacebookStrategy({
clientID: process.env.FACEBOOK_CLIENT_ID,
clientSecret: process.env.FACEBOOK_CLIENT_SECRET,
callbackURL: callbackURLFacebook,
enableProof: true,
profileFields: ['id', 'name', 'email', 'picture.type(large)'],
passReqToCallback: true,
}, verifyCallback))
module.exports = passport
I console.log() things, just to figure out if it falls to the correct sequence of flow, the console doesn't seem to log anything suspicious, is there's something i am missing here?
PS: i am also using next js with custom server
I was facing the same problem and was able to send cookies by using custom callback.
router.get('/google/callback', (req, res) => {
passport.authenticate('google', {session: false, failureRedirect:'/auth/google/failure'},
async(err, user) => {
// You can send cookies and data in response here.
})(req, res)
})
Please refer custom callback section in documentation for explanation.