Trying to log in to gmail while using TestCafe - ssl

I am learning TestCafe and am trying to create an account on a website and then logging in to Gmail to find the activation link. When I try to do this I just get a browser isn't secure message when I get to the part to enter a password. How do I get Gmail to trust TestCafe?

While you might succeed in doing so, this is not a good approach because:
it's slow doing this via GUI
it's britle because selectors will likely change, and you have no control over Google email selectors, so you won't even know if they change them
A better approach wuld be to use a service like Mailosaur where you can create an account and receive emails that you can later query via an API. Instead of doing a whole e2e flow over GUI, you request an email on Mailosaur's API, and if such an email exists, you'll receive a response you can parse and check for various things.
I've done this in the past, you can see my post here: https://sqa.stackexchange.com/questions/40427/automating-verification-of-sent-email-sms-messages/45721#45721 It's exactly Mailosaur and Testcafe (plus it requires axios as a package), so it seems to be what you're looking for.
To add the same code here:
import config from '../config';
import { customAlphabet } from 'nanoid';
import axios from 'axios';
import Newsletter from '../Objects/newsletter';
async function request (reqObject) {
try {
return await axios(reqObject);
} catch (error) {
console.error(error);
}
}
function serverId () {
return process.env.MAILOSAUR_SERVER_ID;
}
function mailosaurFullEmail (id) {
return (id ? id : nanoid()) + '.' + serverId()
+ '#' + config.mailosaurDomain;
}
fixture `Newsletter`
.page(baseUrl);
test
('Sign Up For Newsletter', async t => {
const id = (customAlphabet('1234567890', 10))();
await t
.typeText(Newsletter.newsEmailInput, mailosaurFullEmail(id))
.click(Newsletter.consent)
.click(Newsletter.sendButton);
let res = await request({
method: 'POST',
url: config.mailosaurUrlEmail + serverId(),
headers: {
'Authorization': 'Basic '
+ Buffer.from(process.env.MAILOSAUR_API_KEY)
.toString('base64'),
'Content-Type': 'application/json'
},
data: {
sentTo: mailosaurFullEmail(id)
}
});
await t
.expect(res.status).eql(200);
});
and it requires some config values:
{
"mailosaurUrlEmail": "https://mailosaur.com/api/messages/await?server=",
"mailosaurDomain": "mailosaur.io"
}
This is definitely much better, but it still has some limitations:
Mailosaur's API can still change, so it won't be exactly without any maintenance
it assumes that an email is sent immediately after a user action (newsletter in my case), but that might be far from reality in many situations such as when emails are sent to a queue where it can easily take several minutes to send an email
If you absolutely have to do it via Gmail, you will still be better off looking at their API that should allow you to search and query email messages as well.

There is an issue related to the Google login. You can try turning on the "Allow less secure apps" Google account setting to workaround this issue. Please note that this setting is available for the disabled 2-Step Verification.

Related

Did not receive SMS from Cognito even thought it's a successful request

I'm unable to receive an SMS from using resendConfirmationCode method from amazon-cognito-identity-js. Even thought the request was sent successfully with a sample response of
{
AttributeName: "phone_number",
DeliveryMedium: "SMS",
Destination: "+*******xxxx"
}
I have the following code that will be called once a user submit a form.
function resendConfirmationCode(username) {
const cognitoUser = new CognitoUser({
Username: username,
Pool: userPool,
});
return new Promise((resolve, reject) => {
cognitoUser.resendConfirmationCode(function (err, result) {
if (err) reject(err);
resolve(result);
});
});
}
From my perspective, I think my code works well. I have done signUp, and the SMS got sent if the user was new. However, when I used the resendConfirmationCode I got the above response. I have already verified my phone number in Amazon SNS Sandbox. Is there something I missed?
So I wasn't reading carefully,
Turns out there's a thing called Account spend limit, you can check it on Amazon SNS > Text Messaging (SMS) > Text messaging preferences.
I'm glad that Amazon put $1 on default. Apparently I have no idea that we're charged for SMS verification. (A bit naive, but Amazon got most the free stuff so I thought that's somewhat free too).
Thanks AWS! This information is hard to find thought. Hopefully this helps.

Get a JSON file from an AppScript backend, using an AppScript front end, without getting a CORS error?

I'm trying to build a an API-driven front end in Google AppsScript that calls a REST API hosted on AppScript to make some database queries.
I am currently simply trying to retrieve a JSON file with a GET request.
Everything I try, I get "CORS Missing Allow Origin".
My understand of CORS is that I might experience this with POST request (but maybe there's some people who have phrased their requests to get work this?)
I have a sense that the situation has changed over time, and what has worked in previous SO threads, doesn't seem to work for me now.
Sigh. I feel like Google's Documentation Team would benefit from a dedicated article to explaining how this is supposed to work.
If anyone can shed light on how I can get this to work, I've be most grateful:
client side code:
useEffect(() => {
fetch('https://script.google.com/macros/s/AKfycbz3_hgjZe0E35ZI2mw7aNs3ASkYCct77qIzL_WTOQMu_ZZeax9WpHpPIwm-MFPhZAW77g/exec/get/all', {
redirect: "follow",
headers: {
"Content-Type": "text/plain",
},
})
.then(result => result.json())
.then(rowData => setRowData(rowData))
}, []);
Server side code:
export function doGet(e) {
if (e.pathInfo.startsWith('get/all')) {
return getAllRecords(e);
}
else if (e.pathInfo.startsWith('get')) {
return getRecord(e);
}
else {
return getAllRecords(e);
//return HtmlService.createHtmlOutput('Error: invalid path- ' + e.pathInfo + '\n\n' + e.parameter + e);
}
}
function getAllRecords(e) {
// Connect to the MySQL database using the JDBC connector
const conn = Jdbc.getConnection(url, username, password);
// Construct the SELECT statement
const sql = `SELECT * FROM cars LIMIT 100`;
// Execute the INSERT statement
const stmt = conn.prepareStatement(sql);
const results = stmt.executeQuery();
// Return the inserted record with the generated id
const records = [];
while (results.next()) {
const record = {
id: results.getInt('id'),
name: results.getString('name'),
make: results.getString('make'),
price: results.getInt('price')
};
records.push(record);
}
return ContentService.createTextOutput(JSON.stringify(records)).setMimeType(ContentService.MimeType.TEXT);
// return ContentService.createTextOutput(JSON.stringify(records)).setMimeType(ContentService.MimeType.JAVASCRIPT);
}
I've tried various combination of MIME Type, and request headers and I'll try any combinations people suggest.
In order to use pathInfo, in this case, it is required to use the access token. I thought that this might be the reason for your current issue. But, when the access token is used, I'm worried that is might not be useful for your actual situation. So, in this answer, I would like to propose the following 2 patterns.
Pattern 1:
In this pattern, your script is modified using the access token. In this case, please modify your Javascript as follows.
From:
fetch('https://script.google.com/macros/s/AKfycbz3_hgjZe0E35ZI2mw7aNs3ASkYCct77qIzL_WTOQMu_ZZeax9WpHpPIwm-MFPhZAW77g/exec/get/all', {
redirect: "follow",
headers: {
"Content-Type": "text/plain",
},
})
.then(result => result.json())
.then(rowData => setRowData(rowData))
To:
const accessToken = "###"; // Please set your access token.
fetch('https://script.google.com/macros/s/AKfycbz3_hgjZe0E35ZI2mw7aNs3ASkYCct77qIzL_WTOQMu_ZZeax9WpHpPIwm-MFPhZAW77g/exec/get/all?access_token=' + accessToken)
.then(result => result.json())
.then(rowData => setRowData(rowData))
When you use the access token, please include the scopes of Drive API. Please be careful about this.
Pattern 2:
In this pattern, I would like to propose the modification without using the access token. When the access token cannot be used, unfortunately, pathInfo cannot be used. So, in this pattern, the query parameter is used instead of pathInfo.
Please modify your Javascript as follows.
From:
fetch('https://script.google.com/macros/s/AKfycbz3_hgjZe0E35ZI2mw7aNs3ASkYCct77qIzL_WTOQMu_ZZeax9WpHpPIwm-MFPhZAW77g/exec/get/all', {
redirect: "follow",
headers: {
"Content-Type": "text/plain",
},
})
.then(result => result.json())
.then(rowData => setRowData(rowData))
To:
fetch('https://script.google.com/macros/s/AKfycbz3_hgjZe0E35ZI2mw7aNs3ASkYCct77qIzL_WTOQMu_ZZeax9WpHpPIwm-MFPhZAW77g/exec?value=get%2Fall') // or ?value=get
.then(result => result.json())
.then(rowData => setRowData(rowData))
And also, please modify doGet of your Google Apps Script as follows.
Modified script:
function doGet(e) {
if (e.parameter.value == "get/all") {
return getAllRecords(e);
} else if (e.parameter.value = "get") {
return getRecord(e);
} else {
return getAllRecords(e);
}
}
Note:
In this modification, it supposes that your getAllRecords(e) works fine. Please be careful about this.
And, in this modification, it supposes that your Web Apps is deployed as Execute as: Me and Who has access to the app: Anyone. Please be careful about this.
When you modified the Google Apps Script of Web Apps, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful about this.
You can see the detail of this in my report "Redeploying Web Apps without Changing URL of Web Apps for new IDE (Author: me)".
Thit is a sample modification. So, please modify this for your actual situation.
Reference:
Taking advantage of Web Apps with Google Apps Script (Author: me)

Nuxt SSR - i can't check if a user is authenticated

I'm trying to work on a Nuxt SSR frontend that uses a Django backend with Session Authentication.
I would like to have some SSR pages as well as client rendered pages in my frontend, so i'm using Universal mode.
The problem is that i did not find a working approach to check if a user is authenticated before loading a page, so i can't restrict pages to anonymous users. In order to check if a user is authenticated, Django will check if the request's headers contain a cookie, and according to that return if the user is authenticated or not.
Here is what i tried:
1) Middleware
export default async function ({context, redirect}) {
axios.defaults.withCredentials = true;
return axios({
method: 'get',
url: 'http://127.0.0.1:8000/checkAuth',
withCredentials: true,
}).then(function (response) {
//Redirect if user is authenticated
}).catch(function (error) {
console.log(error)
});
}
Here i'm sending a request to my backend to check if the user is authenticated. The problem is that the middleware is executed from server side, which means there will never be any cookie in the request, even if the user is authenticated. This means that every time i refresh the page, according to the middleware the user is always anonymous, even when the user is authenticated.
2) Plugin
export default function (context, inject) {
if (process.client){
console.log('client')
return axios({
method: 'get',
url: 'http://127.0.0.1:8000/checkAuth',
withCredentials: true,
}).then(function (response) {
//IF AUTHENTICATED, REDIRECT
context.redirect('/')
}).catch(function (error) {
console.log(error)
});
} else {
console.log('server')
}
}
Here i'm trying the same but with a plugin, and i'm "forcing" the plugin to check if the user is authenticated on the backend only when the plugin executes from client side. This works, cookies are sent in the headers and Django receives the cookie, but the problem with this solution is that Nuxt doesn't allow redirecting to other pages from a plugin (https://github.com/nuxt/nuxt.js/issues/4491).
3) Using beforeMount() in Vue
I tried to do that using beforeMount() from my Vue pages, but the problem is that since it will execute AFTER idration, the page will be loaded and after 1/2 seconds the redirect happens, which is kind of ugly.
Is it possible that there isn't a way to do this? Any kind of advice is appreciated
EDIT: the problem is not that i don't know how to code this, the problem is that when Nuxt sends a request to my backend from the server side middleware, the request will not contain any cookie, and because of this my Django backend cannot check the session cookie, which means that the backend cannot check whether or not the user is authenticated. The same code works when the middleware is executed from client side (if i navigate directly to the page instead of refreshing), because the request will contain the cookies.
I'm trying to understand if this is normal or not, but this could be an issue with Nuxt.
I know this a year old question and it was probably about nuxt 2, now nuxt 3 is out and running and I found my self with the same problem and here is how I solved it, just in case someone stumble here just like I did.
With Nuxt 3 server side you can use useFetch with the options headers: useRequestHeaders(['cookie'])
const { data, error, pending, refresh } = await useFetch(api.auth,
{
credentials: "include",
headers: useRequestHeaders(['cookie'])
}
);
There are a few issues you need to be aware of:
_ The cache, if you perform the same request with the same parameters it will return the same cached response (it won't even call the end point API). Try to use the key option with different values or the returned refresh method and check the doc "Data fetching" for more info.
_ The cookies, any cookie generate server side won't be shared with the client side, this means if the API generate a new token or session cookie on server side the browser won't receive those cookies and may generate new ones, this may get you in some 400 - bad request if you use session with CSRF, check this issue for more info.
I do have a working middleware with this
export default ({ redirect, store }) => {
if (store?.$auth?.$state?.loggedIn) {
redirect('https://secure.url')
} else {
redirect('https://login.please')
}
})

How do you test user flows that involve confirmation by email?

I mean functional or E2E testing. That's all clear with generic flows, but when it comes to transactional emails (signup confirmations, password resets, purchase notifications and others) it's still bringing questions. After some research I came up with a few ideas. One is to leverage Restmail.net API (here examples with Selenium WebDriver and Cypress - http://dsheiko.com/weblog/testing-sign-up-flow-with-activation-by-email). It's free, but API is public. So it's not really suitable for email messages with potentially sensitive information. Another approach to access Gmail inbox via IMAP bridge or Gmail API (here the explanation and code snippets - https://docs.puppetry.app/testing-emails/example-with-imap-bridge). But again, it's rather a workaround.
I know there are guys like Sendgrid, Mailgun, Email Yak, Postmark. I don't want to pay that much. So how do you folks do it? It it a thing to you?
We're doing this using Mailosaur email addresses for our test users. We then use a cypress custom command to query Mailosaur for the expected email. It was super easy to set up.
Here's the main part of that custom command, which is all we had to add to start doing email testing. You can refer to their API docs for what query, mailosaurServer, and MailosaurApiKey should be.
Cypress.Commands.add("getEmailFromMailService", query => {
return cy
.request({
method: "POST",
url: `https://mailosaur.com/api/messages/await?server=${mailosaurServer}`,
body: query,
headers: { "Content-Type": "application/json" },
auth: { user: mailosaurApiKey },
})
.then(response => {
expect(response.status).to.equal(200);
return response.body;
});
});
You could create a post request for the "forgot your password" and then assert on it.
something like:
cy.visit('yoursite')
cy.get('#forgotpassword').click().then(function (xhr) {
cy.server()
cy.request('POST', 'APIforForgotPassword').as('sucessfullemail)
})
cy.get(#sucessfullemail).then(function (xhr) {
expect(xhr.status).to.eq(200)
Cypress.Commands.add('ConfirmUser', () => {
const confirmationToken = null;
cy.request({
url: 'http://localhost:3000/api/confirmation_token?email=test_user#cypress.com',
followRedirect: false
})
.then((resp) => {
confirmationToken = resp.token
})
cy.visit('/en/confirmation?confirmation_token=token')
})
Create the API that requires the email as a parameter and returns the confirmation-token. call the API from cypress commands as ajax-request and get the response token

Angular 2 AuthHttp with jwt not connecting

I'm trying to use jwt's authHttp to set an API connection to a particular Back End. I'm trying to make it first without any token so I can test it but it seams like it's not even getting connected. I'm using it as following:
this.authHttp.get('localhost:3001/api/basic')
.subscribe(
data => console.log("data"),
err => console.log(err),
() => console.log('Request Complete')
);
The error I'm getting in the console is AuthHttpError {}
I've set my ngModules as it say in the guide:
providers: [
{
provide: AuthHttp,
useFactory: authHttpServiceFactory,
deps: [Http, RequestOptions]
}
And
function authHttpServiceFactory(http: Http, options: RequestOptions) {
return new AuthHttp(new AuthConfig({noTokenScheme : true}), http);
}
The thing that drive's me crazy is that using http it works fine like this:
this.http.get('http://localhost:3001/api/basic').subscribe(
data=> console.log(data),
error=> console.log("Getting Error")
);
You are probably thinking "Why he is not using http then instead of authHttp?". Well, that's because setting a heather "Authorization" and its token seams impossible with http.
Any help or guidance would be extremely helpful.
If you don't need JsonWebTokens but simply want to add custom headers, you can do it this way without having to import the angular2-jwt library :
In your service :
private customHeaders: Headers = this.setCredentialsHeader();
setCredentialsHeader() {
let headers = new Headers();
let credentials = window.localStorage.getItem('credentials2');
headers.append('Authorization', 'Basic ' + credentials);
return headers;
}
someMethod() {
let url = 'your.URL.to.API';
return this.http
.get(url, { headers: this.customHeaders })
.map(result => {
console.log(result);
});
}
This way you can add your Authorization header with the type of data you want.
If it's a Authorization Bearer type header you are looking for and use it with angular2-jwt, you can use the default configuration first before trying to provide your own AuthHttp instance through the factory. It will be much simpler to debug and figure where the problem is.
From the documentation : https://github.com/auth0/angular2-jwt#configuration-options
AUTH_PROVIDERS gives a default configuration setup:
In your module with your service, just import the AUTH_PROVIDERS like this :
import { AUTH_PROVIDERS } from 'angular2-jwt';
...
#NgModule({
...
providers: [
AUTH_PROVIDERS,
...
]
})
and simply use the AuthHttp instance in your service like you did.
You should see in the Navigator Network tab your headers being added to your request.
EDIT :
As stated in the documentation, it is appending the token value in the headers from the Token Getter Function defined in the AUTH_PROVIDERS by default.
You therefore need to add your JWT in your LocalStorage with the default name id_token.
To give you my working example, I'm setting a JWT upon the authentication process, where I get a JWT as a response from my Http Call :
auth.service.ts
this.identityService.setToken(token.accessToken);
identity.service.ts
setToken(token?) {
if (token) {
window.localStorage.setItem('id_token', token);
} else {
window.localStorage.removeItem('id_token');
}
}
You should be able to see your JWT in your network tab if done correctly.
Afterwards, the AuthHttp instance should add the headers to your requests as intended...
It might not work correctly if your Token is not a JWT. To check if it's a good one, you can use a website such as https://jwt.io/ where it will be decoded.
If it's still not working, this means the problem is coming from elsewhere. A service not provided correctly, etc.