I have a vanilla JavaScript web app that uses the Spotify API and it works fine. I'm in the process of porting it over to a React front-end and NodeJS/Express back-end but I'm receiving this issue. When the user clicks on the "login" button it shows this error in browser console (private info redacted):
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at
https://accounts.spotify.com/authorize?response_type=code&client_id=xxx&
scope=playlist-modify-public%20playlist-modify-private
&redirect_uri=http%3A%2F%2Flocalhost%3A8888%2Fcallback%2F&state=xxxxx&show_dialog=true.
(Reason: CORS header ‘Access-Control-Allow-Origin’ missing).
This is the button in the home page (React):
<button
onClick={login}
type="button"
className="btn btn-primary login-btn col-sm-12 col-lg-6">
Login with Spotify
</button>
async function login() {
console.log("Sent Login Request");
await fetch(`/api/login`, {
method: "GET",
headers: { "Content-Type": "application/json" },
});
}
In the front-end package.json
I have set up proxy:
"proxy": "http://localhost:8888"
This is the app.js
(back-end) code that (I think) is relevant to this problem:
app
.use(express.static(directoryPath))
.use(helmet())
.use(compression())
.use(cors())
.use(cookieParser());
app.use(function (req, res, next) {
res.header("Access-Control-Allow-Origin", "localhost"); // update to match the domain you will make the request from
res.header(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
next();
});
app.get("/api/login", function (req, res) {
console.log("Started LOGIN flow");
let state = generateRandomString(16);
res.clearCookie(stateKey);
res.cookie(stateKey, state);
let scope = "playlist-modify-public playlist-modify-private";
res.redirect(
"https://accounts.spotify.com/authorize?" +
querystring.stringify({
response_type: "code",
client_id: _client_id,
scope: scope,
redirect_uri: _redirect_uri,
state: state,
show_dialog: true, // show dialog to allow users to log out
})
);
});
app.get("/callback", function (req, res) {
let code = req.query.code || null;
let state = req.query.state || null;
let storedState = req.cookies ? req.cookies[stateKey] : null;
if (state === null || state !== storedState) {
res.redirect(
"/#" +
querystring.stringify({
error: "State mismatch",
})
);
} else {
res.clearCookie(stateKey);
// request auth
const params = {
client_id: _client_id,
client_secret: _client_secret,
redirect_uri: _redirect_uri,
code,
grant_type: "authorization_code",
};
ax({
method: "post",
url: "https://accounts.spotify.com/api/token",
params,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
})
.then((response) => {
let token = response.data.access_token;
res.redirect(
"/#" +
querystring.stringify({
authorized: "access_granted",
token: token,
})
);
})
.catch((error) => {
console.log("Failed to login: " + error);
res.redirect(
"/#" + querystring.stringify({ authorized: "access_denied" })
);
});
}
});