I am trying to test my API routes that are protected by using jsonwebtoken.
I have tested my routes in postman and have gotten the correct results however running the same tests using mocha/chai/chai-http is not working.
Everytime I test a protected route I receive a 401 'unauthorized'
describe('user', () => {
let user, token
beforeEach(async () => {
await dropDb()
user = await new User({ email: 'testing#test.com', password: 'password' }).save()
const payload = { userid: user.id, role: user.roles.title, status: user.roles.status }
token = jwt.sign(payload, secret, { expiresIn: 3600 })
})
it('should return a list of users when logged in', async () => {
console.log(token)
const result = await chai.request(app)
.get('/api/user')
.set('Authorization', `Bearer ${token}`)
expect(result).to.have.status(200)
expect(result).to.be.json
})
})
My gut feeling was that there was somehow a race condition where the token being passed into the set wasn't finished being signed before the test ran. But inside the result it appears that my token has been set.
Additionally if I console.log the token after I can see that it matches the token created. Anybody run into a similar issue?
Related
I have created a Cypress command to fetch me a JWT token from my GQL API. I then set the token as a cookie to use in my tests that require authentication.
Here is my login command:
export function login(username, password) {
const { apiUrl: url } = Cypress.env();
cy.request({
method: 'POST',
url,
body: {
operationName: 'signin',
headers: {
'content-type': 'application/json'
},
query:
`mutation signin($email: String!, $password: String!) {
signin(input: {email: $email, password: $password}) {
id
token
__typename
}
}`,
variables: { email: username, password: password }
}
})
.then(response => {
const { token } = response.body.data.signin;
cookie.set('token', token, { expires: 10000000 });
cy.wait(3000);
});
}
I can see when I run the login command the cookie is set but when my test tries to visit a page within my app the cookie disappears.
describe('Entity Page', () => {
before(() => {
const { username, password } = Cypress.env();
cy.login(username, password);
cy.addEntity(newEntityId, {});
cy.wait(3000);
cy.get('#entityId').then(entityId => {
cy.visit(`/entity/${entityId}`)
cy.wait(6000)
});
});
By the time I get to addEntity the cookie disappears and I am unauthenticated. Is there something I need to do to persist cookies? I tried Cypress.Cookies.preserveOnce but this had no effect
I also tried adding the below to my support/index.js but the cookie is still removed.
Cypress.Cookies.defaults({
preserve: 'token'
})
Try it with cy.setCookie(name, value) docs.
It has a couple of defaults that might help.
domain - defaults to window.location.hostname. The domain the cookie is visible to.
expiry - defaults to 20 years into the future. Specified as seconds since 1 January 1970 (10,000,000 is only 115 days).
I'm passing my authentication token via an HTTP-Only cookie in my NestJS API.
As such, when writing some E2E tests for my Auth endpoints, I'm having an issue with cookies not being where I expect them.
Here's my pared-down test code:
describe('auth/logout', () => {
it('should log out a user', async (done) => {
// ... code to create user account
const loginResponse: Response = await request(app.getHttpServer())
.post('/auth/login')
.send({ username: newUser.email, password });
// get cookie manually from response.headers['set-cookie']
const cookie = getCookieFromHeaders(loginResponse);
// Log out the new user
const logoutResponse: Response = await request(app.getHttpServer())
.get('/auth/logout')
.set('Cookie', [cookie]);
});
});
In my JWT Strategy, I'm using a custom cookie parser. The problem I'm having is that request.cookies is always undefined when it gets to the parser. However, the cookie will be present in request.headers.
I'm following the manual cookie example from this Medium article: https://medium.com/#juha.a.hytonen/testing-authenticated-requests-with-supertest-325ccf47c2bb, and there don't appear to be any other methods available on the request object to set cookies.
If I test the same functionality from Postman, everything works as expected. What am I doing wrong?
I know this is an old thread but...
I also had req.cookies undefined, but for a different reason.
I'm testing my router independently, not the top level app. So I bootstrap the app in beforeEach and add the route to test.
I was getting req.cookies undefined because express 4 requires the cookieParser middleware to be present to parse the cookies from the headers.
E.g.
const express = require('express');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const request = require('supertest');
const {router} = require('./index');
describe('router', () => {
let app;
beforeAll(() => {
app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cookieParser());
app.use('/', router);
});
beforeEach(() => jest.clearAllMocks());
it('GET to /', async () => {
const jwt = 'qwerty-1234567890';
const resp = await request(app)
.get('/')
.set('Cookie', `jwt=${jwt};`)
.set('Content-Type', 'application/json')
.send({});
});
});
Testing this way allows me to unit test a router in isolation of the app. The req.cookies turn up as expected.
Late but I hope I can help you. The problem is in the initialization of the app object. Probably in your main.ts file you have some middlewares configured as they are: cors and queryParse. You must also put them in your tests when you create the app.
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
const app = moduleFixture.createNestApplication();
// Add cors
app.enableCors({
credentials: true,
origin: ['http://localhost:4200'],
});
// Add cookie parser
app.use(cookieParser());
await app.init();
As per the article you're following, the code at https://medium.com/#juha.a.hytonen/testing-authenticated-requests-with-supertest-325ccf47c2bb :
1) has the 'cookie' value in .set('cookie', cookie) in lowercase and in your code it's in Pascal case ==> Have you tried with lowercase in your code instead ?
2) the cookie value assigned to the 'cookie' header is not an array, whereas in your code you're assigning an array ==> Have you tried with a non array value ?
So to resume, can you try with the following code:
describe('auth/logout', () => {
it('should log out a user', async (done) => {
// ... code to create user account
const loginResponse: Response = await request(app.getHttpServer())
.post('/auth/login')
.send({ username: newUser.email, password });
// get cookie manually from response.headers['set-cookie']
const cookie = getCookieFromHeaders(loginResponse);
// Log out the new user
const logoutResponse: Response = await request(app.getHttpServer())
.get('/auth/logout')
.set('cookie', cookie) // <== here goes the diff
.expect(200, done);
});
});
Let us know if that helps :)
I have a backend server using Node and Express.js and I'm using Jest as my test framework. I had a problem that I couldn't figure out. Let me share my code first:
// user.model.test.ts
describe('User model test', () => {
let connection: Connection;
beforeEach(async () => {
connection = await createConnection(ormConfig);
});
afterEach(async () => {
connection.close();
});
it('should not create a user with the same email', async () => {
await User.create({
username: 'bar',
email: 'foo#gmail.com',
password: 'password'
}).save();
const user2 = User.create({
username: 'foo',
email: 'foo#gmail.com',
password: 'password'
});
await expect(user2.save()).rejects.toThrowError();
});
}
As you can see here I'm creating a separate connection from my development database here the hooks are attached to test_db.
I have another test on controllers.
user.controller.test.ts
describe('Get all users', () => {
let connection: Connection;
beforeEach(async () => {
connection = await createConnection(ormConfig);
});
afterEach(async () => {
connection.close();
});
it('should return status code 200', async (done) => {
User.create({
username: 'test',
email: 'foobar#gmail.com',
password: 'password'
});
const res = await rekwest.get('/api/v1/users');
expect(res.status).toBe(200);
// console.log(res.st);
done();
});
});
Same thing what I'm doing here. I'm creating a test and attaching to test_db and expecting a different connection pool.
Now the error on the controller is weird User table already exists. And on the model I have an error of test_db doesn't exists. Now if I removed the hooks in the controller test the errors goes away but I can't test the controller.
So I found a link on how to test a MongoDB using Jest we have the same idea but I don't have the runInBand. So I added it jest --runInBand and for some reason, it fixes my errors. Can someone explain to me what happened? In the Jest documentation basically it's saying that your test will run 50% faster but I don't need that or do I?
I'm trying to figure out how to access the accessToken, refreshToken, and idToken that I receive back from aws-amplify using the Auth library.
example in docs: https://aws.github.io/aws-amplify/media/authentication_guide.html
example of my usage:
const user = await Auth.signIn(email, password);
user has a bunch of properties that are inaccessible including everything I need. In the docs, it's unclear how to get to these properties because the examples all log the result. Any ideas?
Auth.currentSession().then(res=>{
let accessToken = res.getAccessToken()
let jwt = accessToken.getJwtToken()
//You can print them to see the full objects
console.log(`myAccessToken: ${JSON.stringify(accessToken)}`)
console.log(`myJwt: ${jwt}`)
})
Auth.currentSession() will return a CognitoUserSession containing accessToken, idToken, and refreshToken.
The CognitoUserSession is actually the following: CognitoUserSession {idToken: CognitoIdToken, refreshToken: CognitoRefreshToken, accessToken: CognitoAccessToken, clockDrift: 0}
Accessing pairs within that object can be achieved through straightforward dot notation at this point.
Example: Retrieve the accessToken and log to console
Auth.currentSession().then(data => console.log(data.accessToken));
The result will be a CognitoAccessToken in the form CognitoAccessToken { jwtToken: '', payload: ''}
If you just want the jwtToken within the CognitoAccessToken, it's just dot notation all the way down (with log to console example):
Auth.currentSession().then(data => console.log(data.accessToken.jwtToken));
Note: This method also refreshes the current session if needed (reference).
I believe you can do
Auth.currentCredentials(credentials => {
const tokens = Auth.essentialCredentials(credentials);
})
where essentialCredentials will return all of the tokens
Hope this helps.
Angular 9, getting JWT token from current session :
import Auth from '#aws-amplify/auth';
Auth.currentSession().then(data => console.log("JWT", data.getAccessToken().getJwtToken()));
For those in search of the AWSCredentials:
const checkCognitoUserSession = async () => {
const getAwsCredentials = await Auth.currentCredentials();
const awsCredentials = await Auth.essentialCredentials(getAwsCredentials);
// accessKeyId, secretAccessKey, sessionToken post login
return { awsCredentials };
};
Retrieve current session using aws-amplify
Auth.currentSession() returns a CognitoUserSession object which contains JWT accessToken, idToken, and refreshToken.
Auth.currentSession()
.then((data) => console.log(data))
.catch((err) => console.log(err));
aws-amplify Docs currentSession
I'm using firebase-admin for authentication on my express backend. I have a middleware that checks if requests are authenticated.
public resolve(): (req, res, next) => void {
return async (req, res, next) => {
const header = req.header('Authorization');
if (!header || !header.split(' ')) {
throw new HttpException('Unauthorized', UNAUTHORIZED);
}
const token = header.split(' ')[1];
await admin.auth().verifyIdToken(token).then((decodedToken: any) => {
req.user = decodedToken;
next();
}).catch((error: any) => {
throw new HttpException(error, UNAUTHORIZED);
});
};
}
So far, I can only unit test my routes to make sure that they respond UNAUTHORIZED instead of NOT_FOUND.
it('GET /api/menu should return 401 ', done => {
const NOT_FOUND = 404;
const UNAUTHORIZED = 401;
supertest(instance)
.get('/api/menu')
.end((error, response: superagent.Response) => {
expect(response.status).not.toEqual(NOT_FOUND);
expect(response.status).toEqual(UNAUTHORIZED);
done();
});
});
But, I want to write more unit tests than this! I want to mock users so I can make AUTHORIZED requests! I want to use the type property I have on users to verify that users of a certain type can/cannot use certain routes. Does anyone have an idea of how I could do this with firebase-admin-node?
It looks like the firebase-admin-node repo generates tokens for unit tests here, but I'm not sure how I would apply that to my specific problem.