How to make apollo server prehandler - apollo

Is there a way to add prehandler for apollo server so that from prehandler i can throw error that apollo will catch and send as response in apollo format? Or maybe there is a helper to format error for response?
How i do it like this with custom getGqlErr helper that will get existing error or generate it:
onst apolloHandler = apolloServer.createHandler({ path: '/api/graphql' })
const apiHandler = async (req: NextApiRequest, res: NextApiResponse): Promise<void> => {
try {
await prehandler({ req, res } as Ctx)
} catch (e) {
res.setHeader('Content-Type', 'application/json')
res.end(
JSON.stringify({
errors: [
{
message: 'Prehandler Error',
extensions: {
langsMsg: getGqlErr(e),
code: 'prehandler custom error',
},
},
],
})
)
return
}
apolloHandler(req, res)
}

It can be done in context function.
const apolloServer = new ApolloServer({
schema,
formatError: (err): any => {
console.log(err)
return err
},
async context(ctx: Ctx): Promise<Ctx> {
await prehandler(ctx)
return ctx
},
})

Related

How to implement auth guard for graphql subscriptions (passportjs + cookies)

How I can pass user to the request?
Is there any possible way to implement something like SubscriptionAuthGuard?
without the subscription, everything works fine
Code:
GraphQLModule.forRoot({
installSubscriptionHandlers: true,
subscriptions: {
'subscriptions-transport-ws': {
onConnect: (connectionParams, webSocket) =>
new Promise((resolve) => {
passportInit(webSocket.upgradeReq, {} as any, () => {
resolve(webSocket.upgradeReq);
});
}),
},
},
context: ({ req }) => ({ req }),
}),
Error:
TypeError: Cannot set property 'authInfo' of undefined
This worked for me, I'm using JWT and bearer tokens.
GraphQL.module:
'subscriptions-transport-ws': {
path: '/graphql',
onConnect: (connectionParams) => {
return {
req: {
headers: { authorization: connectionParams.Authorization },
},
};
},
},
Guard:
#Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
async canActivate(context: ExecutionContext): Promise<boolean> {
try {
return (await super.canActivate(context)) as boolean;
} catch (e) {
throw new AuthenticationError(generalErrorMessages.invalidToken);
}
}
getRequest(context: ExecutionContext): Request {
const ctx = GqlExecutionContext.create(context);
return ctx.getContext().req;
}
}

React Native - Sending token to Django server

So I have this react native code that sends a token in string format, yes I've checked that var token = getAccessToken() is a string and I've console.log it to ensure it is a JWT token as well. But on the Django side when I check request.headers.get('Authorization', None) it outputs: 'Bearer [object Object]' what's going on?
React Native Code
import {Auth} from 'aws-amplify';
export async function getAccessToken() {
try {
const currentUser = await Auth.currentAuthenticatedUser();
console.log(currentUser);
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: ${JSON.stringify(jwt)}`);
console.log(accessToken.jwtToken)
return accessToken.jwtToken
});
} catch (error) {
console.log('error signing up:', error);
}
}
const getPosts = () => {
var token = getAccessToken();
const config = {
headers: { Authorization: `Bearer ` + token }
};
axios
.get(`${url}/posts`, config)
.then(response => {
console.log(response)
setData(response.data);
})
.catch(error => {
console.log(JSON.stringify(error));
});
}
I also tried
const config = {
headers: { Authorization: `Bearer ${token}` }
};
I also tried
function getPosts() {
var token = getAccessToken().then(token => {
const config = {
headers: {
Authorization: `Bearer ${token}`
}
};
console.log(token)
axios
.get(`${url}/posts`, config)
.then(response => {
console.log(response)
setData(response.data);
})
.catch(error => {
console.log(JSON.stringify(error));
});
}).catch(error => {
console.log(JSON.stringify(error));
});;
};
and console.log(token) is outputting "undefined"
Update getAccessToken to return result of
Auth .currentSession()
And
Make getPosts function async and await getAccessToken().
OR
Use the then block to result of promise
getAccessToken().then(token=>{ // Call the api },err=>{ // Handle the error }
Otherwise what you are getting is a promise that's not resolved yet.

hapi authentication strategy karma test with sinon with async function

I am trying to test the authentication scheme with hapi server. I have two helper function within the same file where I put my authentication scheme. I want to test when this successfully authenticate the user. But in my test case I always get 401 which is the unauthenicated message.
export const hasLegitItemUser = async (request, email, id) => {
const {
status,
payload: {users}
} = await svc.getRel(request, email);
if (status !== STATUS.OK) {
return false;
}
return users.includes(user)
};
export const getUser = async request => {
const token = request.state._token;
const res = await svc.validateToken({request, token});
const {
userInfo: {email}
} = res;
const id = extractId(request.path);
const isLetgitUser = await hasLegitItemUser(
request,
email,
id
);
res.isLegitUser = isLegitUser;
return res;
};
const scheme = (server, options) => {
server.state("my_sso", options.cookie);
server.ext("onPostAuth", (request, h) => {
return h.continue;
});
return {
async authenticate(request, h) {
try {
const {
tokenValid,
isLegitUser,
userInfo
} = await getUser(request);
if (tokenValid && isLegitUser) {
request.state["SSO"] = {
TOKEN: request.state._token
};
return h.authenticated({
credentials: {
userInfo
}
});
} else {
throw Boom.unauthorized(null,"my_auth");
}
} catch (err) {
throw Boom.unauthorized(null, "my_auth");
}
}
};
};
My Test file:
import Hapi from "hapi";
import sinon from "sinon";
import auth, * as authHelpers from "server/auth";
import {expect} from "chai";
import pcSvc from "server/plugins/services/pc-svc";
describe("Authentication Plugin", () => {
const sandbox = sinon.createSandbox();
const server = new Hapi.Server();
const authHandler = request => ({
credentials: request.auth.credentials,
artifacts: "boom"
});
before(() => {
server.register({
plugin: auth,
});
const route = ["/mypage/{id}/home"];
route.forEach(path => {
server.route({
method: "GET",
path,
options: {
auth: auth,
handler:{}
}
});
});
});
afterEach(() => {
sandbox.restore();
});
it("should authorize user if it is a validated user", async () => {
sandbox
.stub(authHelpers, "getUser")
.withArgs(request)
.resolves({
tokenValid: true,
isLegitUser: true,
userInfo: {}
});
return server
.inject({
method: "GET",
url:
"/mypage/888/home"
})
.then(res => {
expect(res.statusCode).to.equal(200);
expect(res.result).to.eql({
userInfo: {
email: "abc#gmail.com",
rlUserId: "abc",
userId: "abc#gmail.com"
}
});
});
});
});
I always get the 401 error for unauthenticated. It seems like my "getUser" function in my test is not triggering for some reason, it goes straight to the throw statement in the catch phase in my code. Please help.

mock axios request jest network error

I am trying to create async tests with axios-mock and jest.
This is my test file:
var axios = require('axios');
var MockAdapter = require('axios-mock-adapter');
const middlewares = [thunk,axiosMiddleware]
const mockStore = configureMockStore(middlewares)
describe('async-actions', () => {
var instance;
var mock;
beforeEach(function() {
instance = axios.create();
mock = new MockAdapter(instance);
});
afterEach(() => {
mock.reset()
mock.restore()
})
it('creates FETCH_BOOKINGS_SUCCESS when fetch bookings has been done', () => {
mock
.onGet('/bookings').reply(200, {
data: [
{ id: 1, name: 'test booking' }
]
});
const expectedActions = [
{type: "FETCH_BOOKINGS_START" },
{type: "FETCH_BOOKINGS_SUCCESS", }
]
const store = mockStore({
session: {
token: {
token: "test_token"
}
}
})
return store.dispatch(actions.fetchBookingsTest())
.then(
() => {
expect(store.getActions()).toEqual(expectedActions)
})
// return of async actions
})
})
And my action:
export function fetchBookingsTest() {
return (dispatch) => {
dispatch(async.fetchDataStart(namedType));
return dispatch(rest.get(BOOKINGS))
.then(
(data) => {
dispatch(async.fetchDataSuccess(data,namedType));
},
(error) => {
dispatch(async.fetchDataFailure(error,namedType));
}
)
}
}
I have middleware setup that uses the authentication token from the redux store for each get request. That is why I have setup "test_token" in the mock store.
When I run this test I receive the response
[{"type": "FETCH_BOOKINGS_START"}, {"payload": [Error: Network Error], "type": "FETCH_BOOKINGS_FAILURE"}]
Why am I getting a network error? Do i need to do more setup with Jest to avoid authentication with mock-axios?

How to test Promise catch with Mocha

I'm trying to test the GET HTTP method from a requests module:
const get = (host, resource, options) => {
...
return new Promise((resolve, reject) => fetch(url, opts)
.then(response => {
if (response.status >= 400) {
reject({
message: `[API request error] response status: ${response.status}`,
status: response.status });
}
resolve(response.json());
})
.catch(error => reject(error)));
};
And here is how I tested the .then part:
it('Wrong request should return a 400 error ', (done) => {
let options = { <parameter>: <wrong value> };
let errorJsonResponse = {
message: '[API request error] response status: 400',
status: 400,
};
let result = {};
result = get(params.hosts.api, endPoints.PRODUCTS, options);
result
.then(function (data) {
should.fail();
done();
},
function (error) {
expect(error).to.not.be.null;
expect(error).to.not.be.undefined;
expect(error).to.be.json;
expect(error).to.be.jsonSchema(errorJsonResponse);
done();
}
);
});
However I didn't find a way to test the catch part (when it gives an error and the response status is not >= 400).
Any suggestions?
It would also help me solve the problem a simple example with another code that tests the catch part of a Promise.
I've ended up writing the following code in order to test the catch:
it('Should return an error with invalid protocol', (done) => {
const host = 'foo://<host>';
const errorMessage = 'only http(s) protocols are supported';
let result = {};
result = get(host, endPoints.PRODUCTS);
result
.then(
() => {
should.fail();
done();
},
(error) => {
expect(error).to.not.be.null;
expect(error).to.not.be.undefined;
expect(error.message).to.equal(errorMessage);
done();
}
);
});