RTK Query credentials is [Object: null prototype] - cookies

When I send a request to the server, on server I get an error that cookies = [Object: null prototype].
appAPI code
...
export const appAPI = createApi({
reducerPath: "appAPI",
baseQuery : fetchBaseQuery({
baseUrl : process.env.REACT_APP_API_URL,
credentials : "include",
prepareHeaders: (headers) => {
if (localStorage.getItem("token")) {
headers.set("authorization", `Bearer ${localStorage.getItem("token")}`);
// headers.set("Content-Type", "multipart/form-data");
}
headers.set("Access-Control-Allow-Origin", "https://localhost:3000");
headers.set("Access-Control-Allow-Credentials", "true");
return headers;
},
}),
...
Inside the index.js file cors configuration looks like this
app.use(cors({credentials: true, origin: ["http://localhost:3000"]}));

Related

SvelteKit unable to set cookie

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.

Set Session ID Cookie in Nuxt Auth

I have the following set up in my nuxt.config.js file:
auth: {
redirect: {
login: '/accounts/login',
logout: '/',
callback: '/accounts/login',
home: '/'
},
strategies: {
local: {
endpoints: {
login: { url: 'http://localhost:8000/api/login2/', method: 'post' },
user: {url: 'http://localhost:8000/api/user/', method: 'get', propertyName: 'user' },
tokenRequired: false,
tokenType: false
}
}
},
localStorage: false,
cookie: true
},
I am using django sessions for my authentication backend, which means that upon a successful login, i will have received a session-id in my response cookie. When i authenticate with nuxt however, i see the cookie in the response, but the cookie is not saved to be used in further requests. Any idea what else i need to be doing?
This is how I handled this, which came from a forum post that I cannot find since. First get rid of nuxt/auth and roll your own with vuex store. You will want two middleware, one to apply to pages you want auth on, and another for the opposite.
This assumes you have a profile route and a login route that returns a user json on successful login.
I'm also writing the user to a cookie called authUser, but that was just for debugging and can be removed if you don't need it.
store/index
import state from "./state";
import * as actions from "./actions";
import * as mutations from "./mutations";
import * as getters from "./getters";
export default {
state,
getters,
mutations,
actions,
modules: {},
};
store/state
export default () => ({
user: null,
isAuthenticated: false,
});
store/actions
export async function nuxtServerInit({ commit }, { _req, res }) {
await this.$axios
.$get("/api/users/profile")
.then((response) => {
commit("setUser", response);
commit("setAuthenticated", true);
})
.catch((error) => {
commit("setErrors", [error]); // not covered in this demo
commit("setUser", null);
commit("setAuthenticated", false);
res.setHeader("Set-Cookie", [
`session=false; expires=Thu, 01 Jan 1970 00:00:00 GMT`,
`authUser=false; expires=Thu, 01 Jan 1970 00:00:00 GMT`,
]);
});
}
store/mutations
export const setUser = (state, payload) => (state.user = payload);
export const setAuthenticated = (state, payload) =>
(state.isAuthenticated = payload);
store/getters
export const getUser = (state) => state.user;
export const isAuthenticated = (state) => state.isAuthenticated;
middleware/redirectIfNoUser
export default function ({ app, redirect, _route, _req }) {
if (!app.store.state.user || !app.store.state.isAuthenticated) {
return redirect("/auth/login");
}
}
middleware/redirectIfUser
export default function ({ app, redirect, _req }) {
if (app.store.state.user) {
if (app.store.state.user.roles.includes("customer")) {
return redirect({
name: "panel",
params: { username: app.store.state.user.username },
});
} else if (app.store.state.user.roles.includes("admin")) {
return redirect("/admin/dashboard");
} else {
return redirect({
name: "panel",
});
}
} else {
return redirect("/");
}
}
pages/login- login method
async userLogin() {
if (this.form.username !== "" && this.form.password !== "") {
await this.$axios
.post("/api/auth/login", this.form)
.then((response) => {
this.$store.commit("setUser", response.data);
this.$store.commit("setAuthenticated", true);
this.$cookies.set("authUser", JSON.stringify(response.data), {
maxAge: 60 * 60 * 24 * 7,
});
if (this.$route.query.redirect) {
this.$router.push(this.$route.query.redirect);
}
this.$router.push("/panel");
})
.catch((e) => {
this.$toast
.error("Error logging in", { icon: "error" })
.goAway(800);
The cookie is sent by the server but the client won't read it, until you set the property withCredentials in your client request (about withCredentials read here)
To fix your problem you have to extend your auth config with withCredentials property.
endpoints: {
login: {
url: 'http://localhost:8000/api/login2/',
method: 'post'
withCredentials: true
}
}
Also don't forget to set CORS policies on your server as well to support cookie exchange
Example from ExpressJS
app.use(cors({ credentials: true, origin: "http://localhost:8000" }))
More information about this issue on auth-module github

How to mutate apollo context in next.js?

I've looked in to api-routes-apollo-server-and-client-auth example and not sure how can i mutate context both on ssr and client request.
I want for every graphql resolver (using api) have access to ctx.user object with preparsed JWT token. But where should i parse it?
If i parse it here:
const apolloServer = new ApolloServer({
schema,
context(ctx) {
ctx.user = '123'
return ctx
}
})
export default apolloServer.createHandler({ path: '/api/graphql' })
Then ctx.user will be undefined on SSR request, working only on client request.
You can have something like this:
// server
const apolloServer = new ApolloServer({
schema,
context: async () => {
const jwt = req.headers.authorization
const user = await getUserFromJwt(jwt)
return {
user,
}
}
})
// client
const link = createHttpLink({ uri: 'http://localhost...' })
const authLink = setContext((_, { headers }) => {
return {
headers: {
...headers,
authorization: jwt,
},
}
})
const apolloClient = new ApolloClient({
link: authLink.concat(link),
...
})
Check out these docs: client and server

flutter post request to server with file

Dears, I am new at flutter, I want to send post request from flutter to server
and this is the postman request
Image-Post-Request
Post header:
key Value
Accept: application/json
Content-Type: application/x-www-form-urlencoded
Post Authentication:
Bear Token
Post Body:
key : Value
address : address
description: description
feedback: feedback
media: download.png
I want to make this request from flutter
This is my code:
File _img; // taken by camera
Map<String,String> headers = {
'Content-Type':'application/json',
'Authorization': 'Bearer $token',
};
final msg = jsonEncode({
"address" : _address,
"description": _description,
"feedback" : _techicalSupport,
"media" : _img;
});
try{
var response = await http.post(
"url",
headers: headers,
body: msg,
);
print("${response.toString()}");
}catch(e){
print("${e.toString()}");
}
I got this Error:
Unhandled Exception: Converting object to an encodable object failed: Instance of '_File'
Note: media is not required, when I removed it from the body it works and it create record in the database
I want to include media in the body.
How can I do it please...
This is the answer of my question, I used Dio library :
import 'package:dio/dio.dart';
File myImage;
List<File> _images = [];
// to handle image and make list of images
_handleImage()async{
File imageFile = await ImagePicker.pickImage(source: ImageSource.camera);
if(imageFile != null){
myImage = imageFile;
_images.add(imageFile);
}
}
// for post data with images
void _submit() async{
FormData formData = FormData();
_images.forEach((image) async{
formData.files.addAll(
[
MapEntry(
"media[]",
await dio.MultipartFile.fromFile(image.path),
),
]
);
});
formData.fields.add(MapEntry("address", _address),);
formData.fields.add(MapEntry("description", _description),);
formData.fields.add(MapEntry("feedback", _techicalSupport),);
var response = await new Dio().post(
postUrl,
options: Options(
headers: {
"Content-Type": "application/json",
"Authorization" : 'Bearer $token',
}
),
data: formData
);
print("${response.toString()}");
}
1.Configure FilePicker library (optional step, you may select your file using any library)
Then to pick the file
File file = await FilePicker.getFile();
2.Configure dio library , versions here
import 'package:dio/dio.dart';
then
FormData formData = FormData.fromMap({
"FIELD_NAME_WEBSERVICE_HERE": await MultipartFile.fromFile(imageFile.path,filename: "anyname_or_filename"),
"FIELD_NAME_WEBSERVICE_HERE":"sample value for another field"),
});
var response = await Dio().post("FULL_URL_HERE", data: formData);
print(response);
You need to convert _img to MultipartFile
MultipartFile file=MultipartFile.fromBytes(
'media', await filePath.readAsBytes(),
filename: 'FILE_NAME');
var request = http.MultipartRequest("POST", Uri.parse(url));
if (files != null) request.files.add(file);
You should use MultipartRequest to send file.
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:http_parser/http_parser.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:convert';
void send() async {
final msg = {
"address": "test",
"description": "test",
"feedback": "test",
};
File _img;
var image = await ImagePicker.pickImage(source: ImageSource.camera);
_img = image;
var response = await http.MultipartRequest(
'POST', Uri.parse("url"))
..fields.addAll(msg)
..files.add(http.MultipartFile.fromBytes('image', _img.readAsBytesSync(),
contentType: MediaType('image', 'jpeg'), filename: 'test.jpg'));
print(response);
}

Trying to get then send a cookie using react and fetch

I've been trying to implement some authentication component in my app for a few hours now, and I still don't understand some of the things that are happening.
Basically, I'd like to send a POST request containing some credentials to my API, which sends me a cookie back with a token if the credentials worked. Then, the cookie should be included in the headers of all future requests to my API (which I believed was automatic).
server.js (my API is a mockup for now, with JSON files)
...
app.post('/api/login', jsonParser, (req, res) => {
fs.readFile(ACCOUNTS_FILE, (err, data) => {
if (err) {
console.error(err);
process.exit(1);
}
const accounts = JSON.parse(data);
const credentials = {
email: req.body.email,
password: req.body.password,
};
var token = null;
for (var i = 0; i < accounts.length; ++i) {
const account = accounts[i];
if (account.email === credentials.email
&& account.password === credentials.password) {
token = account.token;
break;
}
}
if (token) {
res.setHeader('Set-Cookie', `access_token=${token}; Secure; HttpOnly;`);
res.json({ token });
} else {
res.json({ token: null });
}
});
});
...
app.js
...
handleConnection(e) {
e.preventDefault();
const email = this.state.email.trim();
const password = this.state.password.trim();
if (!email && !password) {
return (false);
}
fetch(loginUrl, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
credentials: 'include',
},
body: JSON.stringify(this.state),
})
.then((response) => response.json())
.then((data) => {
console.log(data);
})
.catch((error) => {
console.warn(error);
});
return (true);
}
...
Now the console.log(data) always displays my token (or null if my credentials are wrong), but the cookie thing doesn't work...
See, I receive the Set-Cookie header, but I still have no cookie on my page.
And even if I managed to get the cookie, when I try to create a cookie using document.cookie = "access_token=123"; and then send the request again, my cookie doesn't go in my header like it would with a jQuery Ajaxcall :
I read here that adding credentials: 'include' would save the day, but unfortunately it didn't.
What am I missing here?
Thanks in advance!
I had the same problem and I found the answer in Peter Bengtsson's comment here: https://davidwalsh.name/fetch
If I understood, in your case the fetch should be:
fetch(loginUrl, {
credentials: 'same-origin',
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(this.state),
})