I'm building an app with Capacitor JS & Nuxt JS to interface with the Slack API so that I can set my Slack status, I've created a Slack App and have a xoxp- token which works just fine when I hit the endpoint with a POST request via Postman, but from my browser (localhost) and from the running app on my phone I'm getting the following CORS error:
Access to XMLHttpRequest at 'https://slack.com/api/users.profile.set' from origin 'http://localhost:3000' has been blocked by CORS policy: Request header field authorization is not allowed by Access-Control-Allow-Headers in preflight response.
Now this seems silly because you must use the authorization header to provide the Bearer token for authentication, but even after temporarily omitting this, the CORS error remains.
I'm trying to POST to the endpoint for users.profile.set
View another method
What am I missing in my Axios code?
setSlackStatusWithReminder (title, expiry) {
const body = this.convertToQueryString({
profile: this.convertToQueryString(this.profile),
token: 'xoxp-mytoken'
})
this.$axios.post('https://slack.com/api/users.profile.set', body, {
timeout: 10000,
transformRequest(data, headers) {
delete headers.common['Content-Type'];
return data;
}
}).then(res => {
console.log(res)
if (res.data.ok != true) {
alert('something went wrong with the .then')
}
this.isSettingStatus = false
this.actions.isShown = false
}).catch(err => {
this.isSettingStatus = false
this.actions.isShown = false
})
},
UPDATE
I've got a function to convert my request body into a query string from my data, which looks like:
export default {
data () {
return {
profile: {
status_text: '',
status_emoji: '',
status_expiration: 0
}
}
}
}
Query string function to convert body
convertToQueryString (obj) {
const convert = Object.keys(obj)
.map((key, index) => `${key}=${encodeURIComponent(obj[key])}`)
.join('&')
return convert
},
And I'm building it up like:
const body = this.convertToQueryString({
profile: this.convertToQueryString(this.profile),
token: 'xoxp-mytoken'
})
It's giving me an invalid_profile response.
Slack doesn't respond to the pre-flight OPTIONS request with a compatible response.
Avoid the preflight check entirely by ensuring it matches the requirements to be handled as a so-called "simple request".
Notably, ensure the content-type is application/x-www-form-urlencoded, serialize the request body to match and do not use the Authorization header to pass your bearer token, instead pass it as an argument in your request (token).
Not sure why this was so difficult, the following is a valid POST request to the Slack API:
// this.profile -> is the object with the status_* fields
const body = `profile=${JSON.stringify(this.profile)}&token=some_token`
this.$axios.post('https://slack.com/api/users.profile.set', body, {
timeout: 10000,
transformRequest(data, headers) {
delete headers.common['Content-Type'];
return data;
}
}).then(res => {
console.log(err)
}).catch(err => {
console.log(err)
})
Related
Hello everyone it is been 6 hours I am struggling to solve this issue.
I have the following projects:
Client App: ReactJS using axios library
Server App: .NET Core Web api implementing JWT for authorization and authentication.
The Problem:
when trying to send get request from my react application using axios to the backend and attaching the jwt in the header I always get 401 unauthorized.
I tried the same way using postman It works perfectly !!!!!!!!!!
My attempts:
I tried to add the cors to my api and allows every origin, every header, every method still did not work.
Sending Request From ReactJS using axios:
async function getAllUserTasks() {
try {
return axios({
method: "get",
url: "http://localhost:5133/todo/ToDos",
headers: {
Authorization: `Bearer ${localStorage.getItem("jwtToken")}`,
},
body: {
userId: JSON.stringify('924BF80F-F394-4927-8DCC-A7B67AFA663C')
},
});
} catch (error) {
console.log(error);
}
}
//call the function one time
useEffect(() => {
getAllUserTasks();
}, []);
My config for the JWT in .NET app:
services.AddAuthentication(defaultScheme: JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = jwtSettings.Issuer,
ValidAudience = jwtSettings.Audience,
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(jwtSettings.Secret))
});
My config for policy and cors:
services.AddCors(o => o.AddPolicy("MyPolicy", builder =>
{
builder.WithOrigins("http://localhost:3000", "http://localhost:3000/")
.AllowAnyMethod()
.AllowAnyHeader();
}));
This is really frustrating!
Try post request and also get the token from local storage outside of request definition. I think one of theese will fix your problem.
How to send cookie with Axios interceptors, even if withCredentials is set to true?
Error: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
Code:
I have a dummy function that sends a username and password to a serverless API with Azure Functions and NodeJS.
try {
const data = await api.get("/api/getUser")
return data
} catch (error) {
console.log(error)
}
}
The API is an axios config which sets the headers and withCredentials. It connects the interceptors for silent refresh with refresh tokens.
const api = setupInterceptorsTo(
axios.create({
baseURL: "http://localhost:7071",
headers: {
"content-type": "application/x-www-form-urlencoded;charset=utf-8",
"Access-Control-Allow-Origin": "localhost",
},
// withCredentials: true, <-- Uncommenting this causes the problem.
})
)
The interceptors look like this
// onResponse, onRequest, onRequest are omitted here.
const onResponseError = async (error: AxiosError): Promise<AxiosError> => {
if (error.response) {
// Access Token was expired
if (error.response.status === 403) {
try {
const rs = await axios.get(
`http://localhost:7071/api/refreshAccessToken`)
// Gets new access token
const { token } = rs.data.accessToken
localStorage.setItem("token", JSON.stringify(token))
} catch (_error) {
return Promise.reject(_error)
}
}
}
return Promise.reject(error)
}
export const setupInterceptorsTo = (
axiosInstance: AxiosInstance
): AxiosInstance => {
axiosInstance.interceptors.request.use(onRequest, onRequestError)
axiosInstance.interceptors.response.use(onResponse, onResponseError)
return axiosInstance
}
The error no matter what I do comes from uncommenting withCredentials: true in either the axios.create function OR by adding it to the get request to send the refresh token cookie to the backend.
Inspecting the network requests says the Access-Control-Allow-Origin header is set to * despite setting it within the axios.create function.
The cookie returned from /login route is in the set-cookie header.
cookies: [
{
name: "jid",
value: refreshToken,
maxAge: 1000 * 60 * 60 * 24 * 7, // 7 days
httpOnly: true,
},
],
I am out of ideas after trying to fix this for many hours. Advice appreciated.
My API gateway (using serverless) is setup to respond correctly:
function buildResponse(statusCode, body) {
// console.log(body);
return {
statusCode: statusCode,
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Credentials": true
},
body: JSON.stringify(body)
};
}
After deploying the backend to aws, I have client side using React.js with aws amplify, where I make the call:
return API.post("api", "/api");
The problem
I get the token from the api call correctly, but I don't get the status code. How can I build out the api so that I can get the status code as well from API gateway?
Take a look at this from aws amplify docs under "get" for example. If you include the "response" in myInit, you will be able to get the entire axios object including the statuscode.
let apiName = 'MyApiName';
let path = '/path';
let myInit = { // OPTIONAL
headers: {}, // OPTIONAL
response: true, // OPTIONAL (return the entire Axios response object instead of only response.data)
queryStringParameters: { // OPTIONAL
name: 'param'
}
}
API.get(apiName, path, myInit).then(response => {
// Add your code here
}).catch(error => {
console.log(error.response)
});
I'm trying to receive entries from Gravityform Rest API V.2 in Angular by making a get request, except I keep getting 401 http error responses, while my console logs ('Access to XMLHttpRequest at 'livesite' from origin 'localsite' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.'). I have used 'basic authentification' to make the request.
https://docs.gravityforms.com/rest-api-v2/#basic-authentication
https://docs.gravityforms.com/rest-api-v2/#get-forms-form-id-entries
I succesfully used the Postman application to make post/get requests with the same credentials. I have added SSL to my domain and allowed "Access-Control-Allow-Origin: *" in Wordpress.
// Link to form #1
const url = 'https://livesite.com/wp/wp-json/gf/v2/forms/1/entries';
// Base64 key with : and keys are set in environment.ts
const key = btoa(this.publicApiKey + ':' + this.privateApiKey);
// Create HttpHeaders
const headersObject = new HttpHeaders().set('Authorization', 'Basic ' + key);
const httpOptions = { headers: headersObject };
// Get
this.http.get(url, httpOptions)
.subscribe(
response => {
console.log('response', response);
},
error => {
console.log('error', error);
}
);
When making the following fetch request on my front-end I'm getting my desired type and id values.
export const getUserProfile = () => {
return (
fetch(
"https://api.spotify.com/v1/me", {
headers: {"Authorization": "Bearer " + user_id}
})
.then(response => {
return response.json()
})
.then(data => {
console.log(data.type)
console.log(data.id)
})
)
}
Knowing you can't use fetch api in Node I used the npm install request package to get the data on my node server.
request.post(authOptions, function(error, response, body) {
var access_token = body.access_token
let postInfo = {
url: 'https://api.spotify.com/v1/me',
headers: {
"Authoriztion": "Bearer " + access_token
},
json: true
}
request.post(postInfo, function(error, response, body) {
const route = body.type
const current_user_id = body.id
console.log(body)
let uri = process.env.FRONTEND_URI || `http://localhost:3000/${route}/${current_user_id}`
res.redirect(uri + '?access_token=' + access_token)
})
})
The purpose of doing this is so when the res.redirect gets called it sends the client to the user's home page. However when the client gets redirected the url is http://localhost:3000/undefined/undefined?accesss_token={some token}
when looking why the values are undefined I console.log(body) and I get
{
error: {
status: 401,
message: 'No token provided'
}
}
but I can see when logging the response that the token is included
_header: 'POST /v1/me HTTP/1.1\r\nAuthoriztion: Bearer {some token}=\r\nhost: api.spotify.com\r\naccept: application/json\r\ncontent-length: 0\r\nConnection: close\r\n\r\n'
I can see why my values are undefined but why am I getting an unauthorized status in node but not on the client using fetch api? Also I noticed that the url access_token doesn't match the server logged token.
Here are the docs I'm using:
https://www.npmjs.com/package/request
https://developer.spotify.com/documentation/web-api/reference/users-profile/get-current-users-profile/
Github file: https://github.com/ryansaam/litphum-server/blob/master/server.js
If you use node-fetch in your server code, you have a similar API as fetch.