Troubleshooting a CORS problem with connecting an Angular application to a Node server that is accessing the Spotify

I am currently working on setting up an authentication flow using the Spotify API. In this setup, my Angular application is making calls to my Node server which is running on localhost:3000.

export class SpotifyService {
  private apiRoot = 'http://localhost:3000/login';
  constructor(private http: HttpClient) {}

  login() {
    console.log('login');
    this.http.get(this.apiRoot).subscribe((data) => {});
  }
}

The Angular app is hosted on localhost:4000

const express = require('express');
const app = express();
const port = 3000;
const SpotifyWebApi = require('spotify-web-api-node');
const cors = require('cors');

const corsOptions = {
  origin: '*',
  optionsSuccessStatus: 200, // some legacy browsers (IE11, various SmartTVs) choke on 204
};
app.use(cors(corsOptions));

const spotifyApi = new SpotifyWebApi({
  clientId: '...',
  clientSecret: '...',
  redirectUri: 'http://localhost:3000/callback',
});
app.get('/', (req, res) => {
  res.send('Hello World from Node.js server!');
});

app.get('/login', (req, res) => {
  const authorizeUrl = spotifyApi.createAuthorizeURL([
    'user-read-email',
    'user-read-private',
  ], 'state');

  res.redirect(authorizeUrl);
});

// Callback route
app.get('/callback', async (req, res) => {
  const { code } = req.query;
  console.log('code: ', code);

  try {
    const tokens = await spotifyApi.authorizationCodeGrant(code);
    spotifyApi.setAccessToken(tokens.body.access_token);
    spotifyApi.setRefreshToken(tokens.body.refresh_token);
    console.log(tokens.body.access_token);

    // The user is now logged in
    res.redirect('/landing');
  } catch (error) {
    res.status(500).send(error.message);
  }
});


app.listen(port, () => {
  console.log(`Server listening at http://localhost:${port}`);
});

While trying to call the Node server from my Angular application, I encountered the following error:

localhost/:1 Access to XMLHttpRequest at 'https://accounts.spotify.com/authorize?client_id=...&response_type=code&redirect_uri=http://localhost:3000/callback&scope=user-read-email%20user-read-private&state=state' (redirected from 'http://localhost:3000/login') from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

On the Spotify dashboard, my website is registered with localhost:4000 for the Angular app and localhost:3000/callback for the Node server.

I initially thought that the cors() function would handle this issue but seems like there are still CORS restrictions.

Answer №1

Dealing with CORS issue in the communication between an Angular app, a Node server, and the Spotify API

Your current approach involves calling the Angular app to the node server, which then issues a redirect instructing the browser to directly request data from Spotify instead of going through the localhost. However, it is this particular redirection that triggers a cross-origin error.


The login process you are implementing is not meant for Ajax requests.

Here is how it should ideally work:

  1. The browser navigates (possibly via a standard form submission) to /login, exiting the Angular app
  2. Your server redirects them to Spotify's login page
  3. Spotify shows the user a page prompting permission to share personal info (like account name)
  4. The user confirms on Spotify's page, navigating to another Spotify endpoint
  5. Spotify then redirects back to http://localhost:3000/callback with parameters confirming successful login
  6. You respond by relaunching/reloading the Angular app by returning or redirecting to an HTML document

Since Spotify requires showing a webpage to ask for user consent, this process must involve navigating to Spotify's site and not using Ajax requests.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Creating TypeScript domain objects from JSON data received from a server within an Angular app

I am facing a common challenge in Angular / Typescript / JavaScript. I have created a simple class with fields and methods: class Rectangle { width: number; height: number; area(): number { return this.width * this.height; } } Next, I have a ...

Encountering difficulties accessing props while invoking a component in React

In my project, I've created a component called FilterSliders using Material UI. Within this component, I passed a prop named {classes.title} by destructuring the props with const { classes }: any = this.props;. However, when I try to access this prop ...

Issue with Vue plugin syntax causing component not to load

I'm facing an issue with a Vue plugin that I have. The code for the plugin is as follows: import _Vue from "vue"; import particles from "./Particles.vue"; const VueParticles = (Vue: typeof _Vue, options: unknown) => { _Vue. ...

React - Utilizing Secondary Prop Value in Material UI Node Components

I've been working on streamlining my code and am wondering about the best way to pass an additional value using props while fetching data from the backend. I'm utilizing material UI's Autocomplete with the PERN stack. Everything is functioni ...

Using Vivus.js in an Angular 5 Component

I am currently facing some challenges while attempting to incorporate Vivus.js library into Angular 5. The issue seems to be arising from the constructor of Vivus, which suggests that the library is being loaded correctly but not recognizing my element id. ...

Incorporate a hyperlink into a React Material-UI DataGrid

While utilizing the DataGrid component from Material-UI, I am trying to add a link to the end of each row. However, the output is currently displaying as: ( [object Object] ). https://i.stack.imgur.com/2k3q2.png I would like for it to show the record ID, ...

I'm confused, I installed the module but it's still showing an error message saying it can

I've been working on coding a discord music bot, and here is the code I have so far: const config = require('config.json') const Discord = require('discord.js'); const ffmpeg = require('ffmpeg-extra') const client = new ...

Refresh a DIV using two SQL queries in forms

I am encountering an issue with updating a single div element using the results from two different forms on an HTML page. I want either form1 or form2 to display results in the same div element, and it should be updated with one line of content fetched fro ...

Delivering objects from controller in AngularJS

I'm currently working on a project where I need to retrieve objects from the controller. Here's a snippet of my code: score.component.js: angular.module('score').component('score',{ templateUrl : 'app/score/score.t ...

Using Typescript: invoking static functions within a constructor

This is an illustration of my class containing the relevant methods. class Example { constructor(info) { // calling validateInfo(info) } static validateInfo(info):void { // validation of info } I aim to invoke validateInfo ...

Ensure redirect is delayed until async data is fetched

Having come from the Angular world, I found it really easy and convenient to resolve data for routes. However, now that I'm using React, I'm unsure about how to achieve the same functionality. I would like to add an async data loader for my rout ...

Ways to transmit additional arguments to RxJS map function

When working with an Angular application, I utilize HttpClient along with RxJS operators to execute various API calls. One example of this is shown below: return this.httpClient.put(url, JSON.stringify(treeOptions)) .pipe( map(this.extract ...

Invoke a bounded function within an Angular directive using the ng-click event

I was wondering if it's possible to invoke a bound function within a directive by clicking on a specific part of a div. Currently, I have a div with an inner div that acts as a button for expanding the main div. The larger div has a directive associat ...

typescript page objects in protractor are showing an undefined property

Hey there, I'm facing an issue while using POM in Protractor with TypeScript in Angular CLI. The error I'm encountering is "Cannot read property 'sendUsername' of undefined". Since I'm new to TypeScript, can someone guide me on how ...

Leaving the pipeline of route-specific middleware in Express/Node.js

My implementation involves a sequence of "route specific middleware" for this particular route: var express = require('express'); var server = express(); var mw1 = function(req, resp, next) { //perform actions if (suc ...

Disabling a button based on the result of a database query

Is there a way to dynamically enable or disable a button based on the result of a database query in JavaScript? I have managed to display an error message (with id="error") depending on the result, but toggling the button (with id="generate") doesn't ...

What methods can a Java application use to distinguish one browser from another?

Is there a way to determine if the browser being used is Firefox or Chrome? I am looking to create an application that will only run on a specific browser registered by a user. To achieve this, my application needs to be able to identify which browser the ...

The navigation controller, responsible for updating navbar values, is only executed once

My goal is to construct a simple Angular application and familiarize myself with the basics. To start, I'm utilizing Yeoman's angular-generator for scaffolding. This generator comes with a predetermined .config featuring $routeProvider, which I ...

What is the syntax for creating a zip function in TypeScript?

If I am looking to create a zip function: function zip(arrays){ // assume more than 1 array is given and all arrays // share the same length const len = arrays[0].length; const toReturn = new Array(len); for (let i = 0; i < len; i+ ...

Storing input values in the state using Typescript by default

Upon launching, my activeField state is initially empty. However, when a user focuses on the field, it gets added to the state. I am encountering a warning in Typescript because when I attempt to update the selectionEnd of that field, it tells me: Property ...