Spotify OAuth2 Authentication in a NestJS Application

marcus_castanho

Marcus

Posted on March 20, 2022

Spotify OAuth2 Authentication in a NestJS Application

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
Enter fullscreen mode Exit fullscreen mode

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:

Beginning folder tree

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
Enter fullscreen mode Exit fullscreen mode

From now on, there are 3 functionalities that must be available in the application in terms of authentication:

  1. Login of users using the Spotify OAuth2 authorization code flow;
  2. Retrieving the user's info from Spotify and generate a JWT;
  3. 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 the passport-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 the req.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 the user 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 the PassportStrategy using the strategy provided by the passport-spotify lib and name it 'spotify' so the SpotifyOauthGuard can identify it;
  • The constructor method calls the passport-spotify Strategy constructor method using the super method, passing the Spotify app credentials CLIENT_ID and 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;
  • 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 the refreshToken, accessToken and expires_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:

Final folder tree

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

💖 💪 🙅 🚩
marcus_castanho
Marcus

Posted on March 20, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related