Integrate an OAuth2 authorization code flow strategy for the Spotify Web API in a NodeJS with TypeScript and NestJS back end application
When building an API, one of the most important parts of the application is the security and authentication of its users. Most frameworks provide some guidelines on how to implement different authentication strategies. NestJS, for instance, presents, in its official documentation, the JWT strategy.
However, one widely spread authentication strategy is the OAuth2 approach, usually used with 3rd party services such as Facebook, Google and Spotify accounts which provides a way to use an existing account in these services to authenticate the user and even interact with these services on behalf of the authenticated user.
As there is no official documentation for integrating this type of authentication with NestJS and development articles usually focus on Google and Facebook integration, this article presents an alternative to integrate the Spotify Authorization Code Flow with NestJS using the Passport authentication middleware along with the passport-spotify strategy.
This article focuses on the process of using the OAuth2 strategy for Spotify integrated with a NestJS application, therefore, the following requirements are considered to be fulfilled before the process described in this article:
- A NestJS application bootsraped with its basic structure. For this part, it is sufficient to follow the quick setup guide in the NestJS documentation;
- A Spotify account with access to the Spotify Developer Dashboard and an app registered with its CLIENT ID and CLIENT SECRET credentials. Following the step-by-step official documentation on how to use the Spotify API is sufficient for this article.
If you are not familiar with the OAuth2 Authorization Code Flow, please check the guide provided by the Spotify Web API documentation.
The auth folder
With a NestJS application ready, an
auth resource must be created using the following command - considering that the Nest CLI is installed in the machine:
nest g mo auth nest g s auth --no-spec nest g co auth --no-spec
Those commands create an auth folder with basic module, service, and controller files without any .spec files. With all in place, the folder structure should look like this:
Now, the following dependencies must be installed:
npm install @nestjs/passport @nestjs/jwt passport passport-jwt passport-spotify npm install -D @types/passport-jwt @types/passport-spotify
From now on, there are 3 functionalities that must be available in the application in terms of authentication:
- Login of users using the Spotify OAuth2 authorization code flow;
- Retrieving the user's info from Spotify and generate a JWT;
- Using the JWT strategy so that there is no need for connecting with the Spotify OAuth2 server every time there's a need for user authentication in during a session.
For the first and second functionalities described earlier, there needs to be a controller with the routes '/login' and '/redirect':
The above code comprehends the following:
- Both routes, '/login' and '/redirect' are guarded with the
SpotifyOauthGuardcustom guard which implements the
passport-spotifystrategy that will be described later;
loginmethod/route is the endpoint that users will access to initiate the authentication;
spotifyAuthRedirectmethod ('/redirect' route) is the URL to which the Spotify OAuth2 service will be redirected once the user successfully logs in;
spotifyAuthRedirectmethod: retrieves the user's information coming from Spotify located the
req.userproperty - if there is no info, meaning the authentication wasn't performed or failed, the method redirects the request to the login route again - sets the
userreq property to undefined (as it is will be defined as the JWT payload further), generates a JWT with it, and returns the user's info and Spotify tokens that can be used by the application to access routes in the Spotify Web API using the user's info depending on the scopes defined.
The Spotify OAuth2 strategy
When using a built-in passport strategy, a custom guard mustbe created and also its corresponding strategy. The
SpotifyOauthGuard is simply a class that extends the
AuthGuard class, so, after creating a /guards folder, inside it, the
SpotifyOauthGuard should look like this:
Also, the named
spotify strategy must be created inside a /strategies folder:
The above code is responsible for connecting with the Spotify OAuth2 service and managing the redirection to the application. The process is:
SpotifyOauthStrategyclass extends the
PassportStrategyusing the strategy provided by the passport-spotify lib and name it 'spotify' so the
SpotifyOauthGuardcan identify it;
constructormethod calls the passport-spotify
Strategyconstructor method using the
supermethod, passing the Spotify app credentials
CLIENT_SECRET(saved in .env vars as they must not be exposed publicly), better described here, a callback URL which is the same route defined in the auth.controller.ts, '/redirect', and the scopes that the app needs for interacting with the user's information;
supermethod also has a callback function, that will be called as soon as the user's login process succeeds and before it is redirected to the application. This function adds to the request that will be made to the '/redirect' route the following properties: user (containing the user's profile info) and authInfo (containing the
The redirection and JWT generation
Once the strategy is implemented, the user will be redirected to the /redirect URL, and, in
auth.controller.ts (presented earlier), the
spotifyAuthRedirect method will intercept the
req object and extract the
authInfo properties and pass the user to the authService. With the user's info, the
login method in the
AuthService class is responsible for generating the JWT. The auth.service.ts should look like:
auth.service.ts, the '/redirect' route returns an object containing the authInfo and
user properties as well as a header Authentication set to 'Bearer ' concatenated with the JWT.
The JWT strategy
This part of the authentication is basically as described in the official NestJS documentation. For this part, there needs to be defined in your
.env vars a
JWT_SECRET, which is a string that is used to generate and encrypt/decrypt the JWT's that the application generates and must not be exposed publicly. Similar to the Spotify strategy, it's necessary to create a
JwtAuthGuard class that extends the built-in passport AuthGuard along with a corresponding, named 'jwt'. Inside the /guards folder, create the jwt-auth.guard.ts file as the following:
And the corresponding strategy, inside the /strategies folder, should look like:
The above code is executed when a route is decorated with the
super method extracts the JWT provided by the request to a guarded route, decrypts it with the
JWT_SECRET provided and inserts a
user property into the
req object containing the info that was previously inserted into the payload of the JWT.
It's important to highlight that the inserted
user property is not the same that the
spotify-strategy inserts to the
req object, and this is the reason why in the
spotifyAuthRedirect method, the
req.user property is set to undefined before the sign in with the jwt strategy.
Now any authenticate route can be decorated with the
JwtAuthGuard as the following:
The AuthModule and AppModule Configs
With everything in place, it's time to configure the instantiation of all the modules. The
AuthModule class should look like this:
auth.module.ts file defines all the providers of the auth resource and registers the
JWT_SECRET during the instantiation of the
JwtModule as well as the expiration time for it, here defined as 3600s (1 hour).
Also, the AppModule should look like:
app.module.ts instantiates all the modules of the application including the
ConfigModule, which is necessary for using all the env vars described in the process.
The final state of the folders and files of the application should look like this:
The OAuth2 is an interesting way of integrating an application with external apps such as widespread social media services, taking advantage of an easy way to log users as well as providing functionalities to the users related to these apps.
Even though NestJS does not provide an official way of making this kind of integration, there are many open source projects that aims to make it easier this authentication method such as the ones described and used in this article.
- Cover image by Alexander Shatov on Unsplash
Top comments (2)
share github repo please
I've produced the code presented in this tutorial only on gist pages so it could serve to generic purposes. But you can find the whole implementation of it in one of my projects : github.com/marcus-castanho/spotify... . Everything I explained in the article is on the '/src/auth'folder.