unexpected 401 on PATCH request of discord api - discord

I'm trying to programmatically add a role to a user to indicate their account as 'linked' or 'verified' in relation to my web app. To do so, I take the following steps:
in the frontend, send the user to the authorization url to authorize discord
when authorized, discord returns to my redirectURI, which calls my /discord/callback route
in this route, I get an access_token for the user from /oauth2/token
with the access_token, I get the discord_user object from /users/#me/guilds/${GUILD_ID}/member
So far, all of those requests are successful, and I get the data I expect. However, when I try to PATCH the user, and add a new role, for some reason I get a 401 unauthorized error, and I'm not entirely sure why. The endpoint I'm using is: https://discord.com/developers/docs/resources/guild#modify-guild-member
Here's my code for clarity:
const GUILD_ID = '<removed>';
const redirectURI = 'http://localhost:8000/auth/discord/callback';
const encodedRedirectURI = encodeURIComponent(redirectURI);
const url = `https://discord.com/api/oauth2/authorize?response_type=code&client_id=${process.env.DISCORD_CLIENT}&scope=identify%20guilds.members.read%20guilds.join&redirect_uri=${redirectURI}&prompt=consent`
router.get('/discord/callback', async (req, res) => {
const { code } = req.query;
try {
const params = new URLSearchParams();
params.append('grant_type', 'authorization_code');
params.append('client_id', process.env.DISCORD_CLIENT);
params.append('client_secret', process.env.DISCORD_SECRET);
params.append('code', code);
params.append('redirect_uri', 'http://localhost:8000/auth/discord/callback');
let access_token;
try {
const tokenRequest = await fetch(`https://discord.com/api/v8/oauth2/token`, {
method: 'POST',
body: params,
headers: {
'Content-type': 'application/x-www-form-urlencoded'
}
}).then(r => r.json());
access_token = tokenRequest.access_token;
} catch {
throw new Error('Failed to get access token.');
}
let discord_user;
try {
discord_user = await fetch(`https://discord.com/api/v8/users/#me/guilds/${GUILD_ID}/member`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${access_token}`
}
}).then(r => r.json());
} catch {
throw new Error('Failed to get user from guild');
}
const discord_user_id = discord_user.user.id;
try {
await fetch(`https://discord.com/api/v8/guilds/${GUILD_ID}/members/${discord_user_id}`, {
method: 'PATCH',
body: JSON.stringify({
roles: [
'foo',
...discord_user.roles
],
}),
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${access_token}`
}
}).then(r => r.json())
.then((data) => {
console.log(data); // 401 unauthorized
});
} catch {
throw new Error('Failed to add role to user');
}
return res.redirect('/settings?state=success');
} catch {
return res.redirect('/settings?state=failure');
}
});

Figured it out:
The role needs to be the ID of the role, not the name of the role, e.g.: '957197947676803072' instead of 'foo'
The role setting needs to be done by a bot:
await fetch(`https://discord.com/api/v8/guilds/${GUILD_ID}/members/${discord_user_id}`, {
method: 'PATCH',
body: JSON.stringify({
roles: ['957197947676803072', ...discord_user.roles]
}),
headers: {
'Content-Type': 'application/json',
'Authorization': `Bot ${process.env.DISCORD_BOT_TOKEN}`
}
}).then(r => r.json())
I also needed to give that bot account admin permissions and manage roles permissions. You can do this either in the invite link or in your discord's server settings.

Related

Connection with Basic HTTP authorization with email and password body application/x-www-form-urlencoded with axios

I am trying to create a new session with axios following this documentation:
https://www.traccar.org/api-reference/#tag/Session/paths/~1session/post
This is my code, I have really tried everything without results
const sessionurl = 'http://31.220.52.187:8082/api/session';
const params = new URLSearchParams();
params.append('email', 'admin');
params.append('password', 'admin');
const config = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
axios
.post(
sessionurl,
{
withCredentials: true,
headers: {
Accept: 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
'Access-Control-Allow-Origin': '*',
},
},
{
params
},
)
.then(function (response) {
console.log('Authenticated');
})
.catch(function (error) {
console.log('Error on Authentication');
});
It should be something like this:
const params = new URLSearchParams();
params.append('email', 'admin');
params.append('password', 'admin');
axios.post(sessionUrl, params);
You might need to also add a header.

React Remix not sending cookies to remote server

I am trying to set up authentication with Remix as my pure frontend and a django backend.
When the user signs in successfully, the backend sends a cookie with the response and this is set in the browser redirect with remix
const signIn = async (credentials: LoginCreds) => {
try {
const response = await fetch(generateFullBackendUrl('/auth/signin'), {
method: 'POST',
body: JSON.stringify(credentials),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
credentials: 'include'
});
return response;
} catch (e) {
console.log(e);
}
}
const response = await authService.signIn({
email,
password
})
const cookies = response?.headers.get('set-cookie');
if(cookies){
return redirect('profile', {
headers: {
'Set-Cookie': cookies
}
});
However when I try to make subsequent fetch calls in my loader the cookies are not sent to the backend as I would expect the browser would
await fetch(generateFullBackendUrl('api/users/me'), {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
credentials: 'include'
})
Front end is running on port 3000
Backend running on port 4000
Im wondering why the fetch request in the loader does not send the cookies with the request
You need to read the cookie header from the loader request and pass it to your fetch headers.
There’s no way Fetch can automatically know what headers to send when used server side.
There is a quick workaround but not so elegant solution:
in your loader use this:
export const loader: LoaderFunction = async({request}) => {
const response = await fetch(`/api/books?${new URLSearchParams({
limit: '10',
page: '1',
})}`, {
headers: {
'Cookie': request.headers.get('Cookie') || ''
}
});
console.log(response)
if(response.ok) return await response.json();
}

DRF and Knox Authentication: Demo accounts where user doesn't have to input credentials

I'm making an app with React as the front end and am handling authentication with knox. Everything is working fine but I require the ability for users to login to premade demo accounts without inputting any credentials. I can't figure out how to do this with Knox on the backend and I can't have the login info stored in my javascript. Any ideas how to accomplish this?
Knox:
class LoginAPI(KnoxLoginView):
authentication_classes = [BasicLikeAuthentication]
def get_post_response_data(self, request, token, instance):
user = request.user
data = {
'expiry': self.format_expiry_datetime(instance.expiry),
'token': token,
'user': user.username,
'role': user.roles.assigned_role
}
return data
Front end for regular login:
const handleSubmit = (event) => {
event.preventDefault();
const data = new FormData(event.currentTarget);
const credentials = btoa(`${data.get('username')}:${data.get('password')}`);
const requestOptions = {
method: "POST",
credentials: 'include',
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json',
"Authorization": `Basic ${credentials}`
},
body: JSON.stringify({})
}
fetch('http://127.0.0.1:8000/api/login/', requestOptions)
.then(response => {
if (response.status === 401) {
setFailedLogin(true)
}
return response.json()
})
.then(data => {
localStorage.setItem('token', data['token'])
})
.then(fetchCurrentUser)
.then(() => {
localStorage.getItem('role') == "Admin"
? navigate("/manage")
: navigate("/maindash")
})
.catch(error => console.log(error))
}

firebase authentication rest API request with axios

I am trying to write function to Sign in user with Email and Password.
Using Axios and firebase rest API.
So this is how Axios instance looks like, really simple right? ...
const authUrl = `https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=${DATABASE_SECRET}`;
const baseURL = "https://beauty-wonderland-e913c-default-rtdb.firebaseio.com";
export const getAxios = (token = null) => {
const config = {
baseURL: baseURL,
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "DELETE, POST, GET, OPTIONS",
"Access-Control-Allow-Headers":
"Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With",
},
timeout: 10000,
};
if (token !== null) {
// config.headers.Authorization = `Bearer ${token}`;
config.baseURL = authUrl;
config.withCredentials = true;
}
let instance = axios.create(config);
instance.interceptors.request.use(
(request) => {
return request;
},
(error) => {
console.log("axios error: ", error);
return Promise.reject(error);
}
);
instance.interceptors.response.use((response) => {
return response;
});
return instance;
};
This code works fine, flexible and can send any kind of request, but when it comes to authentication, there is problem with sending user data: email and password.
const loginHandler = async () => {
const response = await getAxios("/").post("", {
body: JSON.stringify({
email: "example#example.com",
password: "password",
returnSecureToken: true,
}),
});
const outPut = processResponse(response);
console.log(outPut);
}
so as i guess There is problem with this part
{
body: JSON.stringify({
email: "a#a.com",
password: "123456",
returnSecureToken: true,
}),
});
}
if fetch function works this way
fetch(
`https://identitytoolkit.googleapis.com/v1/accounts:signInWithPasswordkey=${DATABASE_SECRET}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
email: "example#example.com",
password: "password",
returnSecureToken: true,
}),
}
);
why do axios gives following error:
XMLHttpRequest at ... from origin 'http://localhost:19006' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Please note other get and post request with axios works, and alo authentication works with fetch, only axios shows such an error, please post additional resourses to learn more about firebase rest API and Axios usecases.
This is how error looks like
The baseURL in the axios instance returned by "getAxios" function is https://beauty-wonderland-e913c-default-rtdb.firebaseio.com and not the Auth REST API url. It should be authUrl instead. While in fetch you have hard-coded the URL so the URL is correct for sure.
Edit:
Remove those extraneous headers. You just need content-type as per the docs. I got the same CORS error when I had those.
const config = {
baseURL: baseURL,
headers: {
"Content-Type": "application/json",
},
timeout: 10000,
};

axios.post returns bad request of 400 React Native

I'm getting my token from an API but unfortunately my API is returning 400 bad request. I've already checked my api via Postman and it's working fine there. Kindly let me know solution or any mistake.
async componentWillMount(){
axios.post('http://api.myapiurl.com/token', {
grant_type: 'PASSWORD',
username: 'MY_USERNAME',
password: 'MY_PASSWORD'
}, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
}
}).then(response => {
console.log(response.data)
}).catch(err => console.log("api Erorr: ", err.message))
}
error in response below
Request failed with status code 400
- node_modules\axios\lib\core\createError.js:16:24 in createError
- node_modules\axios\lib\core\settle.js:18:6 in settle
- ... 10 more stack frames from framework internals
It is Solved by using QueryString.stringify(). I just pass the body into QueryString.stringify() like below:
axios.post('http://api.apiurl.com/token', QueryString.stringify({
grant_type: 'MY_GRANT_TYPE',
username: 'MY_USERNAME',
password: 'MY_PASSWORD'
}), {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
}
}).then(response => {
console.log(response.data)
}).catch(err => console.log("api Erorr: ", err.response))
From what I can see you are sending json data, but your Content-Type header is set to application/x-www-form-urlencoded; charset=UTF-8. if your api is expecting json then it should be application/json.
try using fetch instead, might be some axios bug, you dont need to add any libraries, here is an example:
fetch("http://api.myapiurl.com/token", {
method: "POST", // *GET, POST, PUT, DELETE, etc.
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
grant_type: "PASSWORD",
username: "MY_USERNAME",
password: "MY_PASSWORD"
})
})
.then(res => {
res.json();
})
.then(data => console.log(data)) // ur data is here
.catch(err => console.log("api Erorr: ", err));
First install the package axios from the url https://www.npmjs.com/package/react-native-axios
Then create two service for handling get and post request so that you can reuse them
GetService.js
import axios from 'axios';
let constant = {
baseurl:'https://www.sampleurl.com/'
};
let config = {
headers: {
'Content-Type': 'multipart/form-data',
'Accept': 'application/json'
}
};
export const GetService = (data,Path,jwtKey) => {
if(jwtKey != ''){
axios.defaults.headers.common['Authorization'] = 'Bearer '+jwtKey;
}
try{
return axios.get(
constant.baseUrl+'api/'+Path,
data,
config
);
}catch(error){
console.warn(error);
}
}
PostService.js
import axios from 'axios';
let constant = {
baseurl:'https://www.sampleurl.com/'
};
let config = {
headers: {
'Content-Type': 'multipart/form-data',
'Accept': 'application/json'
}
};
export const PostService = (data,Path,jwtKey) => {
if(jwtKey != ''){
axios.defaults.headers.common['Authorization'] = 'Bearer '+jwtKey;
}
try{
return axios.post(
constant.baseUrl+'api/'+Path,
data,
config
);
}catch(error){
console.warn(error);
}
}
Sample code for using get and post services is given below
import { PostService } from './PostService';
import { GetService } from './GetService';
let uploadData = new FormData();
uploadData.append('key1', this.state.value1);
uploadData.append('key2', this.state.value2);
//uploadData.append('uploads', { type: data.mime, uri: data.path, name: "samples" });
let jwtKey = ''; // Authentication key can be added here
PostService(uploadData, 'postUser.php', jwtKey).then((resp) => {
this.setState({ uploading: false });
// resp.data will contain json data from server
}).catch(err => {
// handle error here
});
GetService({}, 'getUser.php?uid='+uid, jwtKey).then((resp) => {
// resp.data will contain json data from server
}).catch(err => {
// handle error here
});
Reference from one of my another post Post action API with object parameter within the URL
If you have any doubts, feel free to know

Resources