CORS error persists in oAuth even after setting header

I have a backend express server similar to this, when I hit the route from my React frontend to get the access token I am encountering the following error:

Access to XMLHttpRequest at 'https://www.twitch.tv/login?client_id=my_client_id&redirect_params=client_id%26redirect_uri%3Dhttp%253A%252F%252Flocalhost%253A3000%26response_type%3Dcode%26scope%3Duser_read%26state%3DAO8ZHqkMTJHUG8L61VfCxf7v' (redirected from 'http://localhost:3001/auth/twitch') from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

I have tried setting up Access-Control-Allow-Origin header in the server but that doesn’t help. When I go this the link in the error in new tab then everything works fine without any cors error. What am I doing wrong? How to tackle this?

The first step of oAuth is to redirect the User from your website to Twitch’s, you seem to be trying to fetch the webpage instead of redirecting the user to Twitch

An example using implicit oAuth

The login link will take the user to Twitch.
The user will accept/deny the app
They will come back to your with an access token.

Usually they’ll come back with a code and you exchange the code for a token/refresh token, but this demo is the implicit method that returns only a token

1 Like

@BarryCarlyon Sir I have tried everything but nothing works.
All I want to do is that I have an express server running at port 3001 and react client running at 3000

My Registered URI: http://localhost:3001/auth/twitch/callback
I want to get the oAuth access_token. On the client side first I send the user to registered redirect URI as follows:

const twitchAuth = axios.create({ baseURL: "http://localhost:3001/auth/twitch/callback", });

On the server side,
OAuth2Strategy.prototype.userProfile = function (accessToken, done) {
const options = {
url: “https://api.twitch.tv/helix/users”,
method: “GET”,
headers: {
“Client-ID”: TWITCH_CLIENT_ID,
Accept: “application/vnd.twitchtv.v5+json”,
Authorization: "Bearer " + accessToken,
},
};

request(options, function (error, response, body) {
if (response && response.statusCode == 200) {
done(null, JSON.parse(body));
} else {
done(JSON.parse(body));
}
});
};

passport.serializeUser(function (user, done) {
done(null, user);
});

passport.deserializeUser(function (user, done) {
done(null, user);
});

passport.use(
“twitch”,
new OAuth2Strategy(
{
authorizationURL: “https://id.twitch.tv/oauth2/authorize”,
tokenURL: “https://id.twitch.tv/oauth2/token”,
clientID: TWITCH_CLIENT_ID,
clientSecret: TWITCH_SECRET,
callbackURL: CALLBACK_URL,
state: true,
},
function (accessToken, refreshToken, profile, done) {
profile.accessToken = accessToken;
profile.refreshToken = refreshToken;
done(null, profile);
}
)
);

// Set route to start OAuth link, this is where you define scopes to request
app.get(
“/auth/twitch”,
passport.authenticate(“twitch”, { scope: “user:read:email” })
);

// Set route for OAuth redirect
app.get(
“/auth/twitch/callback”,
(req, res, next) => {
console.log(“hit to callback”);
next();
},
passport.authenticate(“twitch”, {
successRedirect: “/”,
failureRedirect: “/”,
})
);

I have tried setting up header as well as tried using the proxy server but still the CORS error occurs.

Error: Access to XMLHttpRequest at 'https://www.twitch.tv/login?client_id=myclientId&edirect_ur=http://localhost:3001auth/twitch/callback&response_type=code&state%3DQBENkeXou6FJKTSntHiK7H1z' (redirected from 'http://localhost:3001/auth/twitch/callback') from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

I have spent way too much time on this. I don’t know what am I missing. Kindly help

Because it seems you are not redirecting the user to Twitch.

You seem to be using a lib to fetch the URL instead of redirecting.

It may also be a bug with passport since you are using passport

here is a “more simple” example

You have a / missing as the redirect URL is invalid a missing / between 3000 and auth

This section is also wrong.

You’ve got a load of weirdness going on here. And I don’t know if the issue is with PassportJS or something you have done.

For User oAuth authentication

  1. Generate a URL with scopes/etc as needed using response_type=code
  2. Display that URL to the user in the web browser or redirect them to that URL
  3. They will accept/decline the authorisation
  4. They come back with a code exchange the code for an access token/refresh token key pair

You don’t need the kraken v5 header with helix

@BarryCarlyon This is because I edited the actual error message (wanted to remove some jibberish just to make it a bit more readable). My bad.

I got your point but How am I supposed to do that?

One more thing I want to point out is that when I open the server link (http://localhost:3001/auth/twitch/callback) in a new tab, I am redirected to twitch and everything works fine and I am able to get the access_token.
The error only occurs when I try to access the endpoint from the frontend.
I think I know what the problem is but don’t know how to solve it.
Below is the screenshot of my network log:

Here we can see that the authorization request successfully redirects the login/authorization page to the frontend but since this page is sent from twitch and my origin is different so the cors error occurs and the below error is thrown:

I think this is the problem (Point me if I am wrong here). So the only thing which I can’t figure out is that how to redirect to twitch directly from the frontend and at the same time hit my backend API because all my authorization code is written there?

you need to put an actual link on the page for the user to click of HTTP request.

You seem to be doing something else, since your shouldn’t XMLHTTPRequest like this

Here is an implicit auth example

https://barrycarlyon.github.io/twitch_misc/authentication/implicit_auth/

And a “regular” oAuth example

1 Like

This can only happen when we choose implicit oAuth flow. How is this possible in oAuth authorization flow as in that the documentation clearly states in step 1 as follows:
Send the user you want to authenticate to **your registered redirect URI**. Then, an authorization page will ask the user to sign up or log into Twitch and allow the user to choose whether to authorize your application/identity system.

And it makes sense too.

All I am doing is sending the user to my app’s registered redirect URI which is the server endpoint that handles the auth as stated in the documentation. For reference, my server code is similar to this. Either I am doing something wrong or I guess the documentation is a bit misleading.

  1. You need to send the user to Twitch’s OAuth page, as the example in the docs shows:
GET https://id.twitch.tv/oauth2/authorize
    ?client_id=<your client ID>
    &redirect_uri=<your registered redirect URI>
    &response_type=code
    &scope=<space-separated list of scopes>

The user needs to actually go to that page, not any sort of fetch request that tries to display that on your site or anything like that, you need to send the user to that Twitch page.

  1. The user accepts or denies the connection with your app, and are sent back to your redirect uri (https://<your registered redirect URI>/?code=<authorization code>)

  2. Your redirect uri will need a handler that takes the code from the querystring of the incoming request, and exchanges that for an access token and refresh token.

POST https://id.twitch.tv/oauth2/token
    ?client_id=<your client ID>
    &client_secret=<your client secret>
    &code=<authorization code received above>
    &grant_type=authorization_code
    &redirect_uri=<your registered redirect URI>

How is your code different?

Thank you @BarryCarlyon and @Dist. I was able to do this.
As @Dist said I just used anchor tag and set href equal to the authorisation link.
I do have one doubt though, as I was using React and directly using anchor tag results in the full page reload of the app which is I guess not a good thing when developing applications as the state of the application will be lost after the reload. So I do have to research on that.
Issue is resolved though :slight_smile:

But this is a requirement of user oAuth (for any service) you have to leave your site to go to the service site to grant account access.

Depends what you mean by “state”.

If you mean “state” as in the “state parameter for CSRF attack defense”, I store it in the session. And a sessionID cookie is stored on the users computer in order to recall that session

1 Like

If by state you mean things like what page the user is currently on, or some non-persistent data, you could encode that into the state param in Step 1, and when the user is returned to your redirect URL it’ll include that same state param, allowing you to decode whatever state data you put there. This allows for things like if a user tried to load /some/path, but wasn’t logged in, you could store that in the state and when they get back you can return them to /some/path.

This is what can allow the login of a site to be seamless to the end user after the first time they have logged in, as it allows you to send them through the OAuth flow each time their session expires, and have them end up exactly where they are meant to be, and if you don’t use the force_verify param, then as long as the user hasn’t disconnected from your app the whole process will be almost transparent to the user.

1 Like

But a state should still be unique between oAuth requests to prevent CSRF attacks

1 Like

Okay, so to generate the state param I can use some function/ npm package that can convert my app’s state to encoded string, right?

You can just use JSON.stringify(someData) to turn it into a string, and then btoa(yourString) to base64 encode it (and atob(yourBase64String) to decode it).

Keep in mind that there are limits to the max header length of the URL, so depending on the size of what you want to store it may simply not be possible to fit within the state param.

Usually you would do as Barry says and first use the State param to prevent CSRF attacks, and then on top of that add any small amount of state data such as the users location on your site. If you need to store lots of data it would be better to store that data on the server, which the user can retrieve when they load your page (even after coming back from the OAuth flow), or store the data within a cookie or localstorage.

1 Like

Okay thank you.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.