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.
Requirements
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.
The routes
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
SpotifyOauthGuard
custom guard which implements thepassport-spotify
strategy that will be described later; - The
login
method/route is the endpoint that users will access to initiate the authentication; - The
spotifyAuthRedirect
method ('/redirect' route) is the URL to which the Spotify OAuth2 service will be redirected once the user successfully logs in; - The
spotifyAuthRedirect
method: retrieves the user's information coming from Spotify located thereq.user
property - if there is no info, meaning the authentication wasn't performed or failed, the method redirects the request to the login route again - sets theuser
req 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:
- The
SpotifyOauthStrategy
class extends thePassportStrategy
using the strategy provided by the passport-spotify lib and name it 'spotify' so theSpotifyOauthGuard
can identify it; - The
constructor
method calls the passport-spotifyStrategy
constructor method using thesuper
method, passing the Spotify app credentialsCLIENT_ID
andCLIENT_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; - The
super
method 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 therefreshToken
,accessToken
andexpires_in
info).
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 user
and 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:
Finally, in 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 JwtAuthGuard
. 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:
The 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:
The 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:
Conclusion
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.
Credits
- 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.