From 38fc9e7b270138ec54bd98bc3752296de1f914f6 Mon Sep 17 00:00:00 2001 From: Thiago Ramalho Date: Wed, 19 Jun 2024 11:31:09 -0300 Subject: [PATCH 1/4] chore: update doc for refresh --- packages/nestjs-auth-refresh/README.md | 337 ++++++++++++++++++++++++- 1 file changed, 335 insertions(+), 2 deletions(-) diff --git a/packages/nestjs-auth-refresh/README.md b/packages/nestjs-auth-refresh/README.md index e2fed75bb..fb7c02051 100644 --- a/packages/nestjs-auth-refresh/README.md +++ b/packages/nestjs-auth-refresh/README.md @@ -10,6 +10,339 @@ Authenticate requests using JWT refresh tokens passed via the request (headers, [![GH Contrib](https://img.shields.io/github/contributors/conceptadev/rockets?logo=github)](https://github.com/conceptadev/rockets/graphs/contributors) [![NestJS Dep](https://img.shields.io/github/package-json/dependency-version/conceptadev/rockets/@nestjs/common?label=NestJS&logo=nestjs&filename=packages%2Fnestjs-core%2Fpackage.json)](https://www.npmjs.com/package/@nestjs/common) -## Installation +## Table of Contents -`yarn add @concepta/nestjs-auth-refresh` +- [Tutorials](#tutorials) + - [1. Getting Started with AuthRefreshModule](#1-getting-started-with-authrefreshmodule) + - [1.1 Introduction](#11-introduction) + - [Overview of the Library](#overview-of-the-library) + - [Purpose and Key Features](#purpose-and-key-features) + - [1.2 Installation](#12-installation) + - [Install the AuthRefreshModule package](#install-the-authrefreshmodule-package) + - [Add the AuthRefreshModule to Your NestJS Application](#add-the-authrefreshmodule-to-your-nestjs-application) + - [1.3 Basic Setup in a NestJS Project](#13-basic-setup-in-a-nestjs-project) + - [Scenario: Refreshing JWT Tokens](#scenario-refreshing-jwt-tokens) + - [Adding AuthRefreshModule to your NestJS Application](#adding-authrefreshmodule-to-your-nestjs-application) + - [1.4 First Token Refresh](#14-first-token-refresh) + - [Validating the Setup](#validating-the-setup) + - [Step 1: Obtain a Refresh Token](#step-1-obtain-a-refresh-token) + - [Step 2: Refresh the JWT Token](#step-2-refresh-the-jwt-token) + - [Example CURL Calls](#example-curl-calls) + - [Obtain a Refresh Token](#obtain-a-refresh-token) + - [Example Refresh Token Response](#example-refresh-token-response) + - [Refresh the JWT Token](#refresh-the-jwt-token) +- [How-To Guides](#how-to-guides) + - [1. Registering AuthRefreshModule Synchronously](#1-registering-authrefreshmodule-synchronously) + - [2. Registering AuthRefreshModule Asynchronously](#2-registering-authrefreshmodule-asynchronously) + - [3. Global Registering AuthRefreshModule Asynchronously](#3-global-registering-authrefreshmodule-asynchronously) + - [4. Using Custom User Lookup Service](#4-using-custom-user-lookup-service) + - [5. Implementing and Using Custom Token Verification Service](#5-implementing-and-using-custom-token-verification-service) + - [6. Overwriting the Settings](#6-overwriting-the-settings) + - [7. Integration with Other NestJS Modules](#7-integration-with-other-nestjs-modules) +- [Engineering Concepts](#engineering-concepts) + - [Conceptual Overview of JWT Refresh Tokens](#conceptual-overview-of-jwt-refresh-tokens) + - [What is a Refresh Token?](#what-is-a-refresh-token) + - [Benefits of Using Refresh Tokens](#benefits-of-using-refresh-tokens) + - [Design Choices in AuthRefreshModule](#design-choices-in-authrefreshmodule) + - [Why Use NestJS Guards?](#why-use-nestjs-guards) + - [Synchronous vs Asynchronous Registration](#synchronous-vs-asynchronous-registration) + - [Global vs Feature-Specific Registration](#global-vs-feature-specific-registration) + - [Integrating AuthRefreshModule with Other Modules](#integrating-authrefreshmodule-with-other-modules) + - [How AuthRefreshModule Works with AuthJwtModule](#how-authrefreshmodule-works-with-authjwtmodule) + - [Integrating with AuthLocalModule](#integrating-with-authlocalmodule) + + +## Tutorials + +### 1. Getting Started with AuthRefreshModule + +#### 1.1 Introduction + +##### Overview of the Library + +The `AuthRefreshModule` is a powerful yet easy-to-use NestJS module designed for implementing JWT refresh token functionality. With a few simple steps, you can integrate secure token refreshing into your application without hassle. + +##### Purpose and Key Features + +- **Ease of Use**: The primary goal of `AuthRefreshModule` is to simplify the process of adding JWT refresh token functionality to your NestJS application. All you need to do is provide configuration data, and the module handles the rest. +- **Protect By Default**: By default, the `AuthRefreshModule` provides a global `APP_GUARD` to protect all routes by default. This can easily be overridden using the `@AuthPublic` decorator. +- **Synchronous and Asynchronous Registration**: Flexibly register the module either synchronously or asynchronously, depending on your application's needs. +- **Global and Feature-Specific Registration**: Register the module globally or for specific features within your application, allowing for more granular control over authentication and authorization requirements. + +#### 1.2 Installation + +##### Install the AuthRefreshModule package + +To install the `AuthRefreshModule` package, run the following command in your terminal: + +```bash +npm install @concepta/nestjs-auth-refresh +``` + +##### Add the AuthRefreshModule to Your NestJS Application + +To add the `AuthRefreshModule` to your NestJS application, import the module in your main application module (usually `app.module.ts`) and register it using the `forRoot` or `forRootAsync` method: + +```typescript +import { AuthRefreshModule } from '@concepta/nestjs-auth-refresh'; + +@Module({ + imports: [ + AuthRefreshModule.forRoot({ + // Configuration options + }), + ], +}) +export class AppModule {} +``` + +#### 1.3 Basic Setup in a NestJS Project + +##### Scenario: Refreshing JWT Tokens + +To demonstrate the basic setup of the `AuthRefreshModule`, let's consider a scenario +where we want to refresh JWT tokens. In this example, we will use +`@concepta/nestjs-auth-refresh` in conjunction with other essential modules such as +`@concepta/nestjs-auth-jwt`, `@concepta/nestjs-auth-local`, and `@concepta/nestjs-authentication`. +These modules work together to provide a comprehensive and secure token refresh mechanism. + +For more detailed instructions on setting up the authentication modules, please +refer to the [Authentication Module Documentation](#authentication-module-documentation). +We will continue with the tutorial in the [Authentication Module Documentation](#authentication-module-documentation). + +###### Adding AuthRefreshModule to your NestJS Application + +To add the `AuthRefreshModule` to your NestJS application, import the module in your main application module (usually `app.module.ts`) and register it using the `forRoot` or `forRootAsync` method, let's use the `MyJwtUserLookupService` created at [Authentication Module Documentation](#authentication-module-documentation): + +```ts +//... +AuthRefreshModule.forRoot({ + userLookupService: new MyJwtUserLookupService() +}), +//... +``` +>Additionally, you can take advantage of the `UserLookupService` from the `@concepta/nestjs-user` module to streamline user lookup operations within your authentication flow, check [User Module Documentation](#user-module-documentation) for reference: + +By default, `AuthRefreshModule` uses services defined in the +[AuthenticationModule](#nestjs-auth) to verify refresh tokens. However, you can +override this behavior by providing a custom service specifically for the refresh +token implementation during the module setup. + +#### 1.4 First Token Refresh + +##### Validating the Setup + +To validate the setup, let's test the refresh token functionality using CURL commands. + +##### Step 1: Obtain a Refresh Token + +First, obtain a refresh token by sending a request to the `/auth` endpoint with valid credentials: + +```bash +curl -X POST \ + http://localhost:3000/auth/login\ + -H 'Content-Type: application/json' \ + -d '{"username":"user@example.com","password":"password"}' +``` + +This should return a response with an access token and a refresh token. + +##### Step 2: Refresh the JWT Token + +Next, use the obtained refresh token to refresh the JWT token: + +```bash +curl -X POST \ + http://localhost:3000/auth/refresh \ + -H 'Content-Type: application/json' \ + -d '{"refreshToken":""}' +``` + +This should return a new access token and a new refresh token. + +##### Example CURL Calls + +###### Obtain a Refresh Token + +```bash +curl -X POST \ + http://localhost:3000/auth/login \ + -H 'Content-Type: application/json' \ + -d '{"username":"user@example.com","password":"password"}' +``` + +###### Example Refresh Token Response + +```json +{ + "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDg1MjkwMjcsImV4cCI6MTAzMDg1MjkwMjcsImlzcyI6Im5lc3RzLWF1dGgiLCJhdWQiOiJuZXN0cy1hdXRoIiwic3ViIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsImVtYWlsIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsIm5hbWUiOiJjbG91bmNAYXV0aC5jb20iLCJpYXQiOjE3MDg1MjkwMjcsImV4cCI6MTAzMDg1MjkwMjcsImVtYWlsIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsIm5hbWVAIjoiY2xvdW5jYXV0aC5jb20ifQ.UGFzc3dvcmQ=", + "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDg1MjkwMjcsImV4cCI6MTAzMDg1MjkwMjcsImlzcyI6Im5lc3RzLWF1dGgiLCJhdWQiOiJuZXN0cy1hdXRoIiwic3ViIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsImVtYWlsIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsIm5hbWUiOiJjbG91bmNAYXV0aC5jb20iLCJpYXQiOjE3MDg1MjkwMjcsImV4cCI6MTAzMDg1MjkwMjcsImVtYWlsIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsIm5hbWVAIjoiY2xvdW5jYXV0aC5jb20ifQ.UGFzc3dvcmQ=" +} +``` + +###### Refresh the JWT Token + +```bash +curl -X POST \ + http://localhost:3000/auth/refresh \ + -H 'Content-Type: application/json' \ + -d '{"refreshToken":""}' +``` + +###### Response (example): +```json +{ + "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDg1MjkwMjcsImV4cCI6MTAzMDg1MjkwMjcsImlzcyI6Im5lc3RzLWF1dGgiLCJhdWQiOiJuZXN0cy1hdXRoIiwic3ViIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsImVtYWlsIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsIm5hbWUiOiJjbG91bmNAYXV0aC5jb20iLCJpYXQiOjE3MDg1MjkwMjcsImV4cCI6MTAzMDg1MjkwMjcsImVtYWlsIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsIm5hbWVAIjoiY2xvdW5jYXV0aC5jb20ifQ.UGFzc3dvcmQ=", + "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDg1MjkwMjcsImV4cCI6MTAzMDg1MjkwMjcsImlzcyI6Im5lc3RzLWF1dGgiLCJhdWQiOiJuZXN0cy1hdXRoIiwic3ViIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsImVtYWlsIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsIm5hbWUiOiJjbG91bmNAYXV0aC5jb20iLCJpYXQiOjE3MDg1MjkwMjcsImV4cCI6MTAzMDg1MjkwMjcsImVtYWlsIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsIm5hbWVAIjoiY2xvdW5jYXV0aC5jb20ifQ.UGFzc3dvcmQ=" +} +``` + +## How-To Guides + +### 1. Registering AuthRefreshModule Synchronously + +```ts +//... +AuthRefreshModule.register({ + userLookupService: new UserService(), + issueTokenService: new TokenService(), +}), +//... +``` + +### 2. Registering AuthRefreshModule Asynchronously + +```ts +//... +AuthRefreshModule.registerAsync({ + useFactory: async (userService: UserService, tokenService: TokenService) => ({ + userLookupService: userService, + issueTokenService: tokenService, + }), + inject: [UserService, TokenService], +}), +//... +``` + +### 3. Global Registering AuthRefreshModule Asynchronously + +```ts +//... +AuthRefreshModule.forRootAsync({ + useFactory: async (userService: UserService, tokenService: TokenService) => ({ + userLookupService: userService, + issueTokenService: tokenService, + }), + inject: [UserService, TokenService], +}), +//... +``` + +### 4. Using Custom User Lookup Service + +```ts +//... +@Injectable() +export class MyUserLookupService extends AuthRefreshUserLookupServiceInterface { + constructor(private userService: UserService) {} + + async bySubject(subject: ReferenceSubject): Promise { + // return authorized user + return this.userService.findOne(subject); + } +} +//... +``` + +### 5. Implementing and Using Custom Token Verification Service + +By default, `AuthRefreshModule` uses services defined in the +[AuthenticationModule](#nestjs-auth) to verify refresh tokens. However, you can +override this behavior by providing a custom service specifically for the refresh +token implementation during the module setup. + +For more details on implementing a custom token verification service, refer to +section 5 of the How-To Guide in the [@concepta/nestjs-auth-jwt](#nestjs-auth-jwt) documentation. + +### 6. Overwriting the Settings + +```ts +import { ExtractJwt, JwtStrategyOptionsInterface } from "@concepta/nestjs-jwt"; + +const settings: JwtStrategyOptionsInterface = { + jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), + verifyToken: async (token: string, done: (error: any, payload?: any) => void) => { + try { + const payload = { id: 'user-id' }; + done(null, payload); + } catch (error) { + done(error); + } + }, +}; + +// app.module.ts +import { Module } from '@nestjs/common'; +import { AuthRefreshModule } from '@concepta/nestjs-auth-refresh'; + +@Module({ + imports: [ + AuthRefreshModule.registerAsync({ + useFactory: async () => ({ + settings, + }), + }), + ], +}) +export class AppModule {} +``` + +### 7. Integration with Other NestJS Modules + +Integrate `@concepta/nestjs-auth-refresh` with other NestJS modules like `@concepta/nestjs-user`, `@concepta/nestjs-auth-local`, `@concepta/nestjs-auth-jwt`, and more for a comprehensive authentication system. + +## Engineering Concepts + +### Conceptual Overview of JWT Refresh Tokens + +#### What is a Refresh Token? + +A refresh token is a special token used to obtain a new access token without requiring the user to re-authenticate. It is typically issued alongside the access token and has a longer expiration time. + +#### Benefits of Using Refresh Tokens + +- **Improved Security**: By using refresh tokens, access tokens can have shorter lifespans, reducing the risk of token theft. +- **Enhanced User Experience**: Users do not need to log in frequently, as refresh tokens can be used to obtain new access tokens seamlessly. +- **Scalability**: Refresh tokens allow for stateless authentication, which is ideal for scalable applications. + +### Design Choices in AuthRefreshModule + +#### Why Use NestJS Guards? + +NestJS guards provide a way to control access to various parts of the application by checking certain conditions before the route handler is executed. In `AuthRefreshModule`, guards are used to implement authentication and authorization logic. By using guards, developers can apply security policies across routes efficiently, ensuring that only authenticated and authorized users can access protected resources. + +#### Synchronous vs Asynchronous Registration + +The `AuthRefreshModule` supports both synchronous and asynchronous registration: + +- **Synchronous Registration**: This method is used when the configuration options are static and available at application startup. It simplifies the setup process and is suitable for most use cases where configuration values do not depend on external services. + +- **Asynchronous Registration**: This method is beneficial when configuration options need to be retrieved from external sources, such as a database or an external API, at runtime. It allows for more flexible and dynamic configuration but requires an asynchronous factory function. + +#### Global vs Feature-Specific Registration + +The `AuthRefreshModule` can be registered globally or for specific features: + +- **Global Registration**: Makes the module available throughout the entire application. This approach is useful when JWT refresh functionality is required across all or most routes in the application. + +- **Feature-Specific Registration**: Allows the module to be registered only for specific features or modules within the application. This provides more granular control, enabling different parts of the application to have distinct authentication and authorization requirements. + +### Integrating AuthRefreshModule with Other Modules + +#### How AuthRefreshModule Works with AuthJwtModule + +The `AuthRefreshModule` can be seamlessly integrated with the `AuthJwtModule` to provide a comprehensive authentication solution. `AuthJwtModule` handles the initial authentication using JWT tokens. Once the user is authenticated, `AuthRefreshModule` can issue a refresh token that the user can use to obtain new access tokens. This integration allows for secure and efficient authentication processes combining the strengths of both modules. + +#### Integrating with AuthLocalModule + +Integrating `AuthRefreshModule` with `AuthLocalModule` enables the application to handle token refresh logic alongside local authentication. This setup enhances the user experience by maintaining sessions securely and seamlessly. The integration involves configuring both modules to use the same token issuance and verification mechanisms, ensuring smooth interoperability and security. \ No newline at end of file From bf61dc567778d30c379690c2c003e93d7054abaa Mon Sep 17 00:00:00 2001 From: Thiago Ramalho Date: Wed, 19 Jun 2024 13:43:29 -0300 Subject: [PATCH 2/4] chore: update links --- packages/nestjs-auth-refresh/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/nestjs-auth-refresh/README.md b/packages/nestjs-auth-refresh/README.md index fb7c02051..7623ed8bb 100644 --- a/packages/nestjs-auth-refresh/README.md +++ b/packages/nestjs-auth-refresh/README.md @@ -107,12 +107,12 @@ where we want to refresh JWT tokens. In this example, we will use These modules work together to provide a comprehensive and secure token refresh mechanism. For more detailed instructions on setting up the authentication modules, please -refer to the [Authentication Module Documentation](#authentication-module-documentation). -We will continue with the tutorial in the [Authentication Module Documentation](#authentication-module-documentation). +refer to the [Authentication Module Documentation](https://github.com/conceptadev/rockets/tree/main/packages/nestjs-authentication). +We will continue with the tutorial in the [Authentication Module Documentation](https://github.com/conceptadev/rockets/tree/main/packages/nestjs-authentication). ###### Adding AuthRefreshModule to your NestJS Application -To add the `AuthRefreshModule` to your NestJS application, import the module in your main application module (usually `app.module.ts`) and register it using the `forRoot` or `forRootAsync` method, let's use the `MyJwtUserLookupService` created at [Authentication Module Documentation](#authentication-module-documentation): +To add the `AuthRefreshModule` to your NestJS application, import the module in your main application module (usually `app.module.ts`) and register it using the `forRoot` or `forRootAsync` method, let's use the `MyJwtUserLookupService` created at [Authentication Module Documentation](https://github.com/conceptadev/rockets/tree/main/packages/nestjs-authentication): ```ts //... From 5690afa639e4977d8f67e8a5a4fed899dee9e8e0 Mon Sep 17 00:00:00 2001 From: Thiago Ramalho Date: Wed, 19 Jun 2024 17:33:22 -0300 Subject: [PATCH 3/4] chore: improves on docs --- packages/nestjs-auth-refresh/README.md | 27 +++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/packages/nestjs-auth-refresh/README.md b/packages/nestjs-auth-refresh/README.md index 7623ed8bb..4e56e1e7f 100644 --- a/packages/nestjs-auth-refresh/README.md +++ b/packages/nestjs-auth-refresh/README.md @@ -121,13 +121,14 @@ AuthRefreshModule.forRoot({ }), //... ``` ->Additionally, you can take advantage of the `UserLookupService` from the `@concepta/nestjs-user` module to streamline user lookup operations within your authentication flow, check [User Module Documentation](#user-module-documentation) for reference: +>Additionally, you can take advantage of the `MyUserLookupService` from the `@concepta/nestjs-user` module to streamline user lookup operations within your authentication flow, check [User Module Documentation](#user-module-documentation) for reference: By default, `AuthRefreshModule` uses services defined in the [AuthenticationModule](#nestjs-auth) to verify refresh tokens. However, you can override this behavior by providing a custom service specifically for the refresh token implementation during the module setup. + #### 1.4 First Token Refresh ##### Validating the Setup @@ -136,7 +137,7 @@ To validate the setup, let's test the refresh token functionality using CURL com ##### Step 1: Obtain a Refresh Token -First, obtain a refresh token by sending a request to the `/auth` endpoint with valid credentials: +First, obtain a refresh token by sending a request to the `/auth/login` endpoint with valid credentials: ```bash curl -X POST \ @@ -186,7 +187,7 @@ curl -X POST \ curl -X POST \ http://localhost:3000/auth/refresh \ -H 'Content-Type: application/json' \ - -d '{"refreshToken":""}' + -d '{"refreshToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDg1MjkwMjcsImV4cCI6MTAzMDg1MjkwMjcsImlzcyI6Im5lc3RzLWF1dGgiLCJhdWQiOiJuZXN0cy1hdXRoIiwic3ViIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsImVtYWlsIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsIm5hbWUiOiJjbG91bmNAYXV0aC5jb20iLCJpYXQiOjE3MDg1MjkwMjcsImV4cCI6MTAzMDg1MjkwMjcsImVtYWlsIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsIm5hbWVAIjoiY2xvdW5jYXV0aC5jb20ifQ.UGFzc3dvcmQ="}' ``` ###### Response (example): @@ -204,8 +205,8 @@ curl -X POST \ ```ts //... AuthRefreshModule.register({ - userLookupService: new UserService(), - issueTokenService: new TokenService(), + userLookupService: new MyUserLookupService(), + issueTokenService: new MyIssueTokenService(), }), //... ``` @@ -215,11 +216,11 @@ AuthRefreshModule.register({ ```ts //... AuthRefreshModule.registerAsync({ - useFactory: async (userService: UserService, tokenService: TokenService) => ({ - userLookupService: userService, - issueTokenService: tokenService, + useFactory: async (userLookupService: MyUserLookupService, issueTokenService: MyIssueTokenService) => ({ + userLookupService, + issueTokenService, }), - inject: [UserService, TokenService], + inject: [MyUserLookupService, MyIssueTokenService], }), //... ``` @@ -229,11 +230,11 @@ AuthRefreshModule.registerAsync({ ```ts //... AuthRefreshModule.forRootAsync({ - useFactory: async (userService: UserService, tokenService: TokenService) => ({ - userLookupService: userService, - issueTokenService: tokenService, + useFactory: async (userLookupService: MyUserLookupService, issueTokenService: MyIssueTokenService) => ({ + userLookupService, + issueTokenService, }), - inject: [UserService, TokenService], + inject: [MyUserLookupService, MyIssueTokenService], }), //... ``` From 33d02959c536e686679b486ee9314e2b785d9e30 Mon Sep 17 00:00:00 2001 From: Marshall Sorenson Date: Mon, 8 Jul 2024 16:40:26 -0400 Subject: [PATCH 4/4] chore: doc cleanup --- packages/nestjs-auth-refresh/README.md | 182 +++++++++++++++++-------- packages/nestjs-cache/README.md | 2 +- 2 files changed, 124 insertions(+), 60 deletions(-) diff --git a/packages/nestjs-auth-refresh/README.md b/packages/nestjs-auth-refresh/README.md index 4e56e1e7f..53c9d074c 100644 --- a/packages/nestjs-auth-refresh/README.md +++ b/packages/nestjs-auth-refresh/README.md @@ -1,6 +1,7 @@ # Rockets NestJS Refresh Authentication -Authenticate requests using JWT refresh tokens passed via the request (headers, cookies, body, query, etc). +Authenticate requests using JWT refresh tokens passed via +the request (headers, cookies, body, query, etc). ## Project @@ -51,7 +52,6 @@ Authenticate requests using JWT refresh tokens passed via the request (headers, - [How AuthRefreshModule Works with AuthJwtModule](#how-authrefreshmodule-works-with-authjwtmodule) - [Integrating with AuthLocalModule](#integrating-with-authlocalmodule) - ## Tutorials ### 1. Getting Started with AuthRefreshModule @@ -60,20 +60,29 @@ Authenticate requests using JWT refresh tokens passed via the request (headers, ##### Overview of the Library -The `AuthRefreshModule` is a powerful yet easy-to-use NestJS module designed for implementing JWT refresh token functionality. With a few simple steps, you can integrate secure token refreshing into your application without hassle. +The `AuthRefreshModule` is a powerful yet easy-to-use NestJS +module designed for implementing JWT refresh token functionality. +With a few simple steps, you can integrate secure token refreshing +into your application without hassle. ##### Purpose and Key Features -- **Ease of Use**: The primary goal of `AuthRefreshModule` is to simplify the process of adding JWT refresh token functionality to your NestJS application. All you need to do is provide configuration data, and the module handles the rest. -- **Protect By Default**: By default, the `AuthRefreshModule` provides a global `APP_GUARD` to protect all routes by default. This can easily be overridden using the `@AuthPublic` decorator. -- **Synchronous and Asynchronous Registration**: Flexibly register the module either synchronously or asynchronously, depending on your application's needs. -- **Global and Feature-Specific Registration**: Register the module globally or for specific features within your application, allowing for more granular control over authentication and authorization requirements. +- **Ease of Use**: The primary goal of `AuthRefreshModule` is to +simplify the process of adding JWT refresh token functionality to your +NestJS application. All you need to do is provide configuration data, and +the module handles the rest. +- **Synchronous and Asynchronous Registration**: Flexibly register the module +either synchronously or asynchronously, depending on your application's needs. +- **Global and Feature-Specific Registration**: Register the module globally or +for specific features within your application, allowing for more granular +control over authentication and authorization requirements. #### 1.2 Installation ##### Install the AuthRefreshModule package -To install the `AuthRefreshModule` package, run the following command in your terminal: +To install the `AuthRefreshModule` package, run the following command in +your terminal: ```bash npm install @concepta/nestjs-auth-refresh @@ -81,7 +90,9 @@ npm install @concepta/nestjs-auth-refresh ##### Add the AuthRefreshModule to Your NestJS Application -To add the `AuthRefreshModule` to your NestJS application, import the module in your main application module (usually `app.module.ts`) and register it using the `forRoot` or `forRootAsync` method: +To add the `AuthRefreshModule` to your NestJS application, import the module in +your main application module (usually `app.module.ts`) and register it using the +`forRoot` or `forRootAsync` method: ```typescript import { AuthRefreshModule } from '@concepta/nestjs-auth-refresh'; @@ -100,19 +111,24 @@ export class AppModule {} ##### Scenario: Refreshing JWT Tokens -To demonstrate the basic setup of the `AuthRefreshModule`, let's consider a scenario -where we want to refresh JWT tokens. In this example, we will use -`@concepta/nestjs-auth-refresh` in conjunction with other essential modules such as -`@concepta/nestjs-auth-jwt`, `@concepta/nestjs-auth-local`, and `@concepta/nestjs-authentication`. -These modules work together to provide a comprehensive and secure token refresh mechanism. +To demonstrate the basic setup of the `AuthRefreshModule`, let's consider +a scenario where we want to refresh JWT tokens. In this example, we will use +`@concepta/nestjs-auth-refresh` in conjunction with other essential modules +such as `@concepta/nestjs-auth-jwt`, `@concepta/nestjs-auth-local`, and +`@concepta/nestjs-authentication`. These modules work together to provide a +comprehensive and secure token refresh mechanism. -For more detailed instructions on setting up the authentication modules, please -refer to the [Authentication Module Documentation](https://github.com/conceptadev/rockets/tree/main/packages/nestjs-authentication). +For more detailed instructions on setting up the authentication modules, +please refer to the [Authentication Module Documentation](https://github.com/conceptadev/rockets/tree/main/packages/nestjs-authentication). We will continue with the tutorial in the [Authentication Module Documentation](https://github.com/conceptadev/rockets/tree/main/packages/nestjs-authentication). ###### Adding AuthRefreshModule to your NestJS Application -To add the `AuthRefreshModule` to your NestJS application, import the module in your main application module (usually `app.module.ts`) and register it using the `forRoot` or `forRootAsync` method, let's use the `MyJwtUserLookupService` created at [Authentication Module Documentation](https://github.com/conceptadev/rockets/tree/main/packages/nestjs-authentication): +To add the `AuthRefreshModule` to your NestJS application, import the module +in your main application module (usually `app.module.ts`) and register it +using the `forRoot` or `forRootAsync` method, let's use the +`MyJwtUserLookupService` created at +[Authentication Module Documentation](https://github.com/conceptadev/rockets/tree/main/packages/nestjs-authentication): ```ts //... @@ -121,13 +137,16 @@ AuthRefreshModule.forRoot({ }), //... ``` ->Additionally, you can take advantage of the `MyUserLookupService` from the `@concepta/nestjs-user` module to streamline user lookup operations within your authentication flow, check [User Module Documentation](#user-module-documentation) for reference: -By default, `AuthRefreshModule` uses services defined in the -[AuthenticationModule](#nestjs-auth) to verify refresh tokens. However, you can -override this behavior by providing a custom service specifically for the refresh -token implementation during the module setup. +> Additionally, you can take advantage of the `MyUserLookupService` +> from the `@concepta/nestjs-user` module to streamline user lookup +> operations within your authentication flow, check +> [User Module Documentation](#user-module-documentation) for reference: +By default, `AuthRefreshModule` uses services defined in the +[AuthenticationModule](#nestjs-auth) to verify refresh tokens. However, you +can override this behavior by providing a custom service specifically for the +refresh token implementation during the module setup. #### 1.4 First Token Refresh @@ -137,7 +156,8 @@ To validate the setup, let's test the refresh token functionality using CURL com ##### Step 1: Obtain a Refresh Token -First, obtain a refresh token by sending a request to the `/auth/login` endpoint with valid credentials: +First, obtain a refresh token by sending a request to the `/auth/login` +endpoint with valid credentials: ```bash curl -X POST \ @@ -156,7 +176,7 @@ Next, use the obtained refresh token to refresh the JWT token: curl -X POST \ http://localhost:3000/auth/refresh \ -H 'Content-Type: application/json' \ - -d '{"refreshToken":""}' + -d '{"refreshToken":"[refresh_token_value]"}' ``` This should return a new access token and a new refresh token. @@ -176,8 +196,8 @@ curl -X POST \ ```json { - "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDg1MjkwMjcsImV4cCI6MTAzMDg1MjkwMjcsImlzcyI6Im5lc3RzLWF1dGgiLCJhdWQiOiJuZXN0cy1hdXRoIiwic3ViIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsImVtYWlsIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsIm5hbWUiOiJjbG91bmNAYXV0aC5jb20iLCJpYXQiOjE3MDg1MjkwMjcsImV4cCI6MTAzMDg1MjkwMjcsImVtYWlsIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsIm5hbWVAIjoiY2xvdW5jYXV0aC5jb20ifQ.UGFzc3dvcmQ=", - "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDg1MjkwMjcsImV4cCI6MTAzMDg1MjkwMjcsImlzcyI6Im5lc3RzLWF1dGgiLCJhdWQiOiJuZXN0cy1hdXRoIiwic3ViIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsImVtYWlsIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsIm5hbWUiOiJjbG91bmNAYXV0aC5jb20iLCJpYXQiOjE3MDg1MjkwMjcsImV4cCI6MTAzMDg1MjkwMjcsImVtYWlsIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsIm5hbWVAIjoiY2xvdW5jYXV0aC5jb20ifQ.UGFzc3dvcmQ=" + "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cC...", + "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cC..." } ``` @@ -187,14 +207,15 @@ curl -X POST \ curl -X POST \ http://localhost:3000/auth/refresh \ -H 'Content-Type: application/json' \ - -d '{"refreshToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDg1MjkwMjcsImV4cCI6MTAzMDg1MjkwMjcsImlzcyI6Im5lc3RzLWF1dGgiLCJhdWQiOiJuZXN0cy1hdXRoIiwic3ViIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsImVtYWlsIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsIm5hbWUiOiJjbG91bmNAYXV0aC5jb20iLCJpYXQiOjE3MDg1MjkwMjcsImV4cCI6MTAzMDg1MjkwMjcsImVtYWlsIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsIm5hbWVAIjoiY2xvdW5jYXV0aC5jb20ifQ.UGFzc3dvcmQ="}' + -d '{"refreshToken":"eyJhbGciOiJIUzI1NiIsInR5cC..."}' ``` -###### Response (example): +###### Response (example) + ```json { - "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDg1MjkwMjcsImV4cCI6MTAzMDg1MjkwMjcsImlzcyI6Im5lc3RzLWF1dGgiLCJhdWQiOiJuZXN0cy1hdXRoIiwic3ViIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsImVtYWlsIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsIm5hbWUiOiJjbG91bmNAYXV0aC5jb20iLCJpYXQiOjE3MDg1MjkwMjcsImV4cCI6MTAzMDg1MjkwMjcsImVtYWlsIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsIm5hbWVAIjoiY2xvdW5jYXV0aC5jb20ifQ.UGFzc3dvcmQ=", - "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDg1MjkwMjcsImV4cCI6MTAzMDg1MjkwMjcsImlzcyI6Im5lc3RzLWF1dGgiLCJhdWQiOiJuZXN0cy1hdXRoIiwic3ViIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsImVtYWlsIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsIm5hbWUiOiJjbG91bmNAYXV0aC5jb20iLCJpYXQiOjE3MDg1MjkwMjcsImV4cCI6MTAzMDg1MjkwMjcsImVtYWlsIjoiY2xvdW5jYUBleGFtcGxlLmNvbSIsIm5hbWVAIjoiY2xvdW5jYXV0aC5jb20ifQ.UGFzc3dvcmQ=" + "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cC...", + "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cC..." } ``` @@ -216,11 +237,14 @@ AuthRefreshModule.register({ ```ts //... AuthRefreshModule.registerAsync({ - useFactory: async (userLookupService: MyUserLookupService, issueTokenService: MyIssueTokenService) => ({ + inject: [MyUserLookupService, MyIssueTokenService], + useFactory: async ( + userLookupService: MyUserLookupService, + issueTokenService: MyIssueTokenService + ) => ({ userLookupService, issueTokenService, }), - inject: [MyUserLookupService, MyIssueTokenService], }), //... ``` @@ -230,11 +254,14 @@ AuthRefreshModule.registerAsync({ ```ts //... AuthRefreshModule.forRootAsync({ - useFactory: async (userLookupService: MyUserLookupService, issueTokenService: MyIssueTokenService) => ({ + inject: [MyUserLookupService, MyIssueTokenService], + useFactory: async ( + userLookupService: MyUserLookupService, + issueTokenService: MyIssueTokenService + ) => ({ userLookupService, issueTokenService, }), - inject: [MyUserLookupService, MyIssueTokenService], }), //... ``` @@ -257,20 +284,24 @@ export class MyUserLookupService extends AuthRefreshUserLookupServiceInterface { ### 5. Implementing and Using Custom Token Verification Service -By default, `AuthRefreshModule` uses services defined in the -[AuthenticationModule](#nestjs-auth) to verify refresh tokens. However, you can -override this behavior by providing a custom service specifically for the refresh -token implementation during the module setup. +By default, `AuthRefreshModule` uses services defined in the +[AuthenticationModule](#nestjs-auth) to verify refresh tokens. However, you +can override this behavior by providing a custom service specifically for +the refresh token implementation during the module setup. -For more details on implementing a custom token verification service, refer to -section 5 of the How-To Guide in the [@concepta/nestjs-auth-jwt](#nestjs-auth-jwt) documentation. +For more details on implementing a custom token verification service, refer to +section 5 of the How-To Guide in the +[@concepta/nestjs-auth-jwt](#nestjs-auth-jwt) documentation. ### 6. Overwriting the Settings ```ts -import { ExtractJwt, JwtStrategyOptionsInterface } from "@concepta/nestjs-jwt"; + // app.module.ts +import { Module } from '@nestjs/common'; +import { ExtractJwt } from '@concepta/nestjs-jwt'; +import { AuthRefreshModule, AuthRefreshSettingsInterface } from '@concepta/nestjs-auth-refresh'; -const settings: JwtStrategyOptionsInterface = { +const settings: AuthRefreshSettingsInterface = { jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), verifyToken: async (token: string, done: (error: any, payload?: any) => void) => { try { @@ -282,10 +313,6 @@ const settings: JwtStrategyOptionsInterface = { }, }; -// app.module.ts -import { Module } from '@nestjs/common'; -import { AuthRefreshModule } from '@concepta/nestjs-auth-refresh'; - @Module({ imports: [ AuthRefreshModule.registerAsync({ @@ -300,7 +327,10 @@ export class AppModule {} ### 7. Integration with Other NestJS Modules -Integrate `@concepta/nestjs-auth-refresh` with other NestJS modules like `@concepta/nestjs-user`, `@concepta/nestjs-auth-local`, `@concepta/nestjs-auth-jwt`, and more for a comprehensive authentication system. +Integrate `@concepta/nestjs-auth-refresh` with other NestJS modules +like `@concepta/nestjs-user`, `@concepta/nestjs-auth-local`, +`@concepta/nestjs-auth-jwt`, and more for a comprehensive +authentication system. ## Engineering Concepts @@ -308,42 +338,76 @@ Integrate `@concepta/nestjs-auth-refresh` with other NestJS modules like `@conce #### What is a Refresh Token? -A refresh token is a special token used to obtain a new access token without requiring the user to re-authenticate. It is typically issued alongside the access token and has a longer expiration time. +A refresh token is a special token used to obtain a new access token +without requiring the user to re-authenticate. It is typically issued +alongside the access token and has a longer expiration time. #### Benefits of Using Refresh Tokens -- **Improved Security**: By using refresh tokens, access tokens can have shorter lifespans, reducing the risk of token theft. -- **Enhanced User Experience**: Users do not need to log in frequently, as refresh tokens can be used to obtain new access tokens seamlessly. -- **Scalability**: Refresh tokens allow for stateless authentication, which is ideal for scalable applications. +- **Improved Security**: By using refresh tokens, access tokens can + have shorter lifespans, reducing the risk of token theft. +- **Enhanced User Experience**: Users do not need to log in frequently, +as refresh tokens can be used to obtain new access tokens seamlessly. +- **Scalability**: Refresh tokens allow for stateless authentication, +which is ideal for scalable applications. ### Design Choices in AuthRefreshModule #### Why Use NestJS Guards? -NestJS guards provide a way to control access to various parts of the application by checking certain conditions before the route handler is executed. In `AuthRefreshModule`, guards are used to implement authentication and authorization logic. By using guards, developers can apply security policies across routes efficiently, ensuring that only authenticated and authorized users can access protected resources. +NestJS guards provide a way to control access to various parts of the +application by checking certain conditions before the route handler is +executed. In `AuthRefreshModule`, guards are used to implement authentication +and authorization logic. By using guards, developers can apply security +policies across routes efficiently, ensuring that only authenticated +and authorized users can access protected resources. #### Synchronous vs Asynchronous Registration -The `AuthRefreshModule` supports both synchronous and asynchronous registration: +The `AuthRefreshModule` supports both synchronous and asynchronous +registration: -- **Synchronous Registration**: This method is used when the configuration options are static and available at application startup. It simplifies the setup process and is suitable for most use cases where configuration values do not depend on external services. +- **Synchronous Registration**: This method is used when the configuration +options are static and available at application startup. It simplifies the +setup process and is suitable for most use cases where configuration values +do not depend on external services. -- **Asynchronous Registration**: This method is beneficial when configuration options need to be retrieved from external sources, such as a database or an external API, at runtime. It allows for more flexible and dynamic configuration but requires an asynchronous factory function. +- **Asynchronous Registration**: This method is beneficial when configuration +options need to be retrieved from external sources, such as a database or an +external API, at runtime. It allows for more flexible and dynamic +configuration but requires an asynchronous factory function. #### Global vs Feature-Specific Registration The `AuthRefreshModule` can be registered globally or for specific features: -- **Global Registration**: Makes the module available throughout the entire application. This approach is useful when JWT refresh functionality is required across all or most routes in the application. +- **Global Registration**: Makes the module available throughout the entire +application. This approach is useful when JWT refresh functionality is +required across all or most routes in the application. -- **Feature-Specific Registration**: Allows the module to be registered only for specific features or modules within the application. This provides more granular control, enabling different parts of the application to have distinct authentication and authorization requirements. +- **Feature-Specific Registration**: Allows the module to be registered +only for specific features or modules within the application. This provides +more granular control, enabling different parts of the application to have +distinct authentication and authorization requirements. ### Integrating AuthRefreshModule with Other Modules #### How AuthRefreshModule Works with AuthJwtModule -The `AuthRefreshModule` can be seamlessly integrated with the `AuthJwtModule` to provide a comprehensive authentication solution. `AuthJwtModule` handles the initial authentication using JWT tokens. Once the user is authenticated, `AuthRefreshModule` can issue a refresh token that the user can use to obtain new access tokens. This integration allows for secure and efficient authentication processes combining the strengths of both modules. +The `AuthRefreshModule` can be seamlessly integrated with the +`AuthJwtModule` to provide a comprehensive authentication solution. +`AuthJwtModule` handles the initial authentication using JWT tokens. + +Once the user is authenticated, `AuthRefreshModule` can issue a refresh +token that the user can use to obtain new access tokens. This integration +allows for secure and efficient authentication processes combining the +strengths of both modules. #### Integrating with AuthLocalModule -Integrating `AuthRefreshModule` with `AuthLocalModule` enables the application to handle token refresh logic alongside local authentication. This setup enhances the user experience by maintaining sessions securely and seamlessly. The integration involves configuring both modules to use the same token issuance and verification mechanisms, ensuring smooth interoperability and security. \ No newline at end of file +Integrating `AuthRefreshModule` with `AuthLocalModule` enables the +application to handle token refresh logic alongside local authentication. +This setup enhances the user experience by maintaining sessions securely and +seamlessly. The integration involves configuring both modules to use the same +token issuance and verification mechanisms, ensuring smooth interoperability +and security. diff --git a/packages/nestjs-cache/README.md b/packages/nestjs-cache/README.md index cc98f5d6d..355a52b5f 100644 --- a/packages/nestjs-cache/README.md +++ b/packages/nestjs-cache/README.md @@ -324,7 +324,7 @@ curl -X GET http://your-api-url/cache/user/{id} 3. **Update (PUT)**: To update an existing cache entry, the request body should match the `CacheUpdatableInterface`: -```sh +```ts export interface CacheUpdatableInterface extends Pick { expiresIn: string | null; }