I'm trying to set cookies in apollo client.
I'm setting the response cookie which works fine, also working on graphql playground with setting "request.credentials": "same-origin", or "include" and it stores cookies successfully.
My problem starts when I'm trying to store cookies from FE app (React, Apollo client). When i put the credentials: "same-origin" the app is working but the cookies are not saved. And when i use "include" the queries are cors blocked.
FRONTEND
const RouterComponent: FC = () => {
const cache = useMemo(() => {
return new InMemoryCache();
}, []);
const client = useMemo(() => {
const token = localStorage.getItem('token') || 'token';
const httpLink = createHttpLink({
uri: 'http://localhost:4000/graphql',
credentials: 'include',
headers: {
authorization: token ? `Bearer ${token}` : '',
},
});
const link = from([
httpLink,
createUploadLink({
uri: 'http://localhost:4000/graphql',
}),
]);
return new ApolloClient({
link,
cache,
typeDefs,
});
}, [cache]);
return (
<ApolloProvider client={client}>
<Switch>
<Route exact path="/" component={MainPage} />;
<Route exact path="/:category" component={Category} />;
</Switch>
</ApolloProvider>
);
};
QUERY
games: (_: any, args: any, {req, res}: any) => {
const refresh_token = sign({userId: "123"}, "secret", {expiresIn: "7d"})
const access_token = sign({userId: "123"}, "secret", {expiresIn: "15min"})
res.cookie('refresh-token', refresh_token, {expire: 60 * 60 * 24 * 7}) // 7 days
res.cookie('access-token', access_token, {expire: 60 * 15}) // 15 mins
return Game.find()
}
SERVER.TS
const { ApolloServer } = require( 'apollo-server-express');
const {typeDefs} = require( './typeDefs');
const {resolvers} = require( './resolvers');
const express = require( 'express');
const cors = require( 'cors');
const path = require( 'path');
const bodyParser = require( 'body-parser');
const mongoose = require("mongoose");
const dotenv = require('dotenv');
const MONGO_CONNECTION = ""
const app = express();
export default (async function () {
try {
await mongoose.connect(MONGO_CONNECTION, {
useNewUrlParser: true,
useUnifiedTopology: true})
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req, res }: any) => {
return {
req,
res
};
},
});
const corsConfig = {
origin: '*',
credentials: true,
allowedHeaders: ['Content-Type', 'Authorization'],
};
dotenv.config();
app.use(function (req: any, res: any, next: any) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
res.setHeader('Access-Control-Allow-Credentials', true);
next();
});
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use('/images', express.static(path.join(__dirname, 'images')));
const dir = path.join(process.cwd(), 'images');
app.use(express.static(dir));
app.use(express.static('public'))
app.use(express.static('files'))
server.applyMiddleware({ app, cors: corsConfig });
app.listen({ port: 4000 }, () =>
console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`)
)
} catch (err) {
console.error(err);
}
})();
thank you for any help
Related
Using SvelteKit 1.0.0-next.571
My application has a login route with:
+page.server.ts => redirects to / if locals.user is set
+page.svelte => show login page
signin/+server.ts => Login and get a jwt from graphql app running on the same machine.
+server.ts:
[..]
let gqlResponse = await response.json();
if ( gqlResponse.errors ) {
console.log("ERRORS FROM GRAPHQL MIDDLEWARE:", gqlResponse.errors);
return json( { error: gqlResponse.errors, isException: true } );
}
if (gqlResponse.data.login.user && !gqlResponse.data.login.error) {
opts.cookies.set('jwt', gqlResponse.data.login.token, {
path: '/',
maxAge: SESSION_MAX_AGE
});
opts.setHeaders( { 'Access-Control-Allow-Credentials': 'true' } )
opts.setHeaders({ 'Content-Type': 'application/json' })
}
return json( gqlResponse.data.login );
and the login handler in +page.svelte :
[..]
const fetchOptions = {
method: 'POST',
//mode: 'no-cors',
//redirect: 'follow' as RequestRedirect,
body: JSON.stringify(credentials),
credentials: 'include' as RequestCredentials
}
try {
const response = await fetch('/login/signin', fetchOptions);
const login = await response.json();
if (login.error) {
handleError(login);
return false;
}
} catch (e) {
return handleException(e);
}
goto('/', { replaceState: true, invalidateAll: true} );
This works fine in localhost, but connecting another device to the local network does not set any cookies making impossible to login:
Local: http://localhost:5173/
➜ Network: http://192.168.x.x:5173/
I also tried with different fetch options and cookie settings like:
opts.cookies.set('jwt', gqlResponse.data.login.token, {
path: '/',
httpOnly: true,
sameSite: 'strict',
// secure: true
maxAge: SESSION_MAX_AGE
});
but no luck, and now 'm stuck.
I have a problem when performing a login service, I did the test in postman and I don't get the expected result, instead it throws me a "500 Internal Server Error", I really don't know what could be wrong.
auth.js file
const router = require("express").Router();
const user = require("../models/user");
const CryptoJS = require("crypto-js");
const jwt = require("jsonwebtoken");
//Login Operation
router.post("/login", async (req, res) => {
try
{
const user = await user.findOne({ email: req.body.email });
if(!user) return res.status(401).json("Correo o Contraseña incorrectos");
const bytes = CryptoJS.AES.decrypt(user.password, process.env.SECRET_KEY);
const originalPassword = bytes.toString(CryptoJS.enc.Utf8);
if(originalPassword !== req.body.password) return
res.status(401).json("Correo o Contraseña incorrectos");
const accessToken = jwt.sign(
{ id: user._id, isAdmin: user.isAdmin },
process.env.SECRET_KEY,
{ expiresIn: "5d" }
);
const { password, ...info } = user._doc;
res.status(200).json({ ...info, accessToken });
}catch(err)
{
res.status(500).json(err);
}
});
module.exports = router;
index.js file
const express = require("express" );
const dotenv = require("dotenv" );
const moongose = require("mongoose");
const app = express();
//Aplication Routes
const authRoute = require("./routes/auth");
dotenv.config();
moongose
.connect(process.env.MONGODB_URL, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => console.log("MongoDB Connection :Active"))
.catch((err) => console.log(err));
app.use(express.json());
//Use Routes
app.use("/api/auth", authRoute);
app.listen(8800, () => {
console.log("Backend Server :Active");
});
The best way to understand the error is to log it on the console.
Inside the catch block, insert the following code snippet:
console.log("Error Here: " err);
This would tell you much more about the error than just its status code.
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.
I work with an application built with Nextjs and Apollo. I receive a token into the Graphql response headers. I can read this token on the server side and I need to pass it to the Apollo Provider on the client side.
cache.writeData({
data: {
isLoggedIn: false
}
});
const afterwareLink = new ApolloLink((operation, forward) => {
return forward(operation).map(response => {
const context = operation.getContext();
const {
response: { headers },
} = context;
if (headers) {
const authorization = headers.get('authorization');
if (authorization) {
console.log(authorization);
// what to do next? I need somehow set isLoggedIn to true...
}
}
return response;
});
});
const link = ApolloLink.from([
afterwareLink,
new RetryLink(),
httpLink
]);
const client = new ApolloClient({
link,
cache
});
function App({ children }){
return (
<ApolloProvider client={client}>
{children}
</ApolloProvider>
);
}
I have tried to set the context inside de ApolloLink:
operation.setContext({ isLoggedIn: true });
I can't write directly on the cache because the page is rendered on the server side.
OS: Windows 10 Pro
express: 4.17.1
apollo-server-express: 2.9.13
apollo-client: 2.6.4
apollo-link-context: 1.0.18
apollo-link-http: 1.5.15
apollo-link-ws: 1.0.18
So, I'm in the process of migrating from graphql-yoga to apollo-server 2 and am experiencing ws connection issues (See image). What am I overlooking?
My code is as follows:
withData.js
const endpoint = `http://localhost:4444/graphql`;
const endpointWS = `ws://localhost:4444/subscriptions`;
const httpLink = createHttpLink({
uri: process.env.NODE_ENV === 'development' ? endpoint : prodEndpoint,
credentials: 'include',
});
const wsLink = process.browser ? new WebSocketLink({
uri: process.env.NODE_ENV === 'development' ? endpointWS : prodEndpointWS,
options: {
reconnect: true,
timeout: 3000,
}
}) : null;
const authLink = setContext(() => {
return {
headers: {
...headers,
}
}
});
const link = process.browser ? split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
wsLink,
httpLink,
) : httpLink;
index.js
const PORT = '4444';
const path2 = '/graphql';
const createServer = require('./createServer');
const server = createServer();
const app = express();
app.use(cookieParser());
server.applyMiddleware({
app,
path: path2,
cors: {
credentials: true,
origin: process.env.FRONTEND_URL,
},
});
const httpServer = http.createServer(app);
server.installSubscriptionHandlers(httpServer);
httpServer.listen(PORT, err => {
if (err) throw err
console.log(`🚀 Server ready at http://localhost:${PORT}${server.graphqlPath}`)
console.log(`🚀 Subscriptions ready at ws://localhost:${PORT}${server.subscriptionsPath}`)
});
createServer.js
const Mutation = require('./resolvers/Mutation');
const Query = require('./resolvers/Query');
const Subscription = require('./resolvers/Subscription');
const db = require('./db');
const typeDefsFile = importSchema(__dirname.concat('/schema.graphql'));
const typeDefs = gql(typeDefsFile);
function createServer() {
return new ApolloServer({
typeDefs,
resolvers: {
Mutation,
Query,
Subscription,
},
cors: {
credentials: true,
origin: process.env.FRONTEND_URL,
},
subscriptions: {
keepAlive: 1000,
path: '/subscriptions',
},
playground: process.env.NODE_ENV === 'production' ? false : '/',
tracing: true,
introspection: true,
context: req => ({ ...req, db }),
});
}
module.exports = createServer;
db.js
const { Prisma } = require('prisma-binding');
const db = new Prisma({
typeDefs: __dirname + "/schema_prep.graphql",
endpoint: process.env.PRISMA_ENDPOINT,
secret: process.env.PRISMA_SECRET,
debug: false,
});
module.exports = db;
Subscriptions.js
const Subscription = {
item: {
subscribe: async (parent, args, ctx, info) => {
const itemResult = await ctx.db.subscription
.item({
where: {
mutation_in: ['CREATED', 'UPDATED'],
},
},
info
);
return itemResult;
},
},
itemDeleted: {
subscribe: (parent, args, ctx, info) => {
const selectionSet = `{ previousValues { id, userIdentity } }`
return ctx.db.subscription.item(
{
where: {
mutation_in: ['DELETED'],
},
},
selectionSet,
);
},
resolve: (payload, args, context, info) => {
return payload ? payload.item.previousValues : payload
},
},
};
module.exports = Subscription;
I resolved this issue by changing the response and request attributes of the context in my query and mutation resolvers from ctx.response and ctx.request to ctx.res and ctx.req respectively.