Implement Refresh Token Automatic Reuse Detection without cluttering your database
Tags: webdev, tutorial, node, typescript
Published: 2022-06-07 18:47:20

While studying how to implement refresh tokens rotation in a [Node.js](https://nodejs.org) [project](https://github.com/alvaromrveiga/ecommerce-backend) I came into this blog post from Auth0: [What Are Refresh Tokens and How to Use Them Securely](https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/). In the section where they explain about Refresh Token Automatic Reuse Detection it is said:

> The 🚓 Auth0 Authorization Server has been keeping track of all the refresh tokens descending from the original refresh token. That is, it has created a "token family".
<sub>[Refresh Token Automatic Reuse Detection section](https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/#Refresh-Token-Automatic-Reuse-Detection)</sub>

But if the tokens are never compromised and the application is used regularly by many users that would mean lots of inactive refreshed tokens cluttering the database before expiration.

## A Solution
You can add a family property in your refresh tokens model in the database, this is my model using [Prisma ORM](https://www.prisma.io/):

```typescript
model UserTokens {
  id String @id @default(uuid())

  user   User   @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId String

  refreshToken String
  family       String   @unique
  browserInfo  String? // Show the user logged devices 
  expiresAt    DateTime
  createdAt    DateTime @default(now())
}
```

The family receives a v4 UUID when the user logs in and a brand new refresh token is created. 
The tokenFamily is added to the refresh token payload for future refreshes:

<sub>In the following code snippets I'm using [NestJS framework](https://nestjs.com/) and [TypeScript](https://www.typescriptlang.org/)</sub>

```typescript
  /** Creates the refresh token and saves it in the database */
  private async createRefreshToken(
    payload: {
      sub: string;
      tokenFamily?: string;
    },
    browserInfo?: string,
  ): Promise<string> {
    if (!payload.tokenFamily) { 
      payload.tokenFamily = uuidV4();
    }

    const refreshToken = await this.jwtService.signAsync(
      { ...payload },
      refreshJwtConfig,
    );

    await this.saveRefreshToken({
      userId: payload.sub,
      refreshToken,
      family: payload.tokenFamily,
      browserInfo,
    });

    return refreshToken;
  }
``` 

Now that we have our refreshToken created and stored we can use it to refresh the accessToken and rotate the current refreshToken. But first we need to validate it:

```typescript
  /** Checks if the refresh token is valid */
  private async validateRefreshToken(
    refreshToken: string,
    refreshTokenContent: RefreshTokenPayload,
  ): Promise<boolean> {
    const userTokens = await this.prismaService.userTokens.findMany({
      where: { userId: refreshTokenContent.sub, refreshToken },
    });

    const isRefreshTokenValid = userTokens.length > 0;

    if (!isRefreshTokenValid) {
      await this.removeRefreshTokenFamilyIfCompromised(
        refreshTokenContent.sub,
        refreshTokenContent.tokenFamily,
      );

      throw new InvalidRefreshTokenException();
    }

    return true;
  }

  /** Removes a compromised refresh token family from the database
   *
   * If a token that is not in the database is used but it's family exists
   * that means the token has been compromised and the family should me removed
   *
   * Refer to https://auth0.com/docs/secure/tokens/refresh-tokens/refresh-token-rotation#automatic-reuse-detection
   */
  private async removeRefreshTokenFamilyIfCompromised(
    userId: string,
    tokenFamily: string,
  ): Promise<void> {
    const familyTokens = await this.prismaService.userTokens.findMany({
      where: { userId, family: tokenFamily },
    });

    if (familyTokens.length > 0) {
      await this.prismaService.userTokens.deleteMany({
        where: { userId, family: tokenFamily },
      });
    }
  }
```
If the token is invalid but the family exists that means this is a token descending from the original refreshToken, so that family was compromised and should be removed.

## Conclusion
To implement Refresh Token Rotation Automatic Reuse Detection without storing all refresh tokens descending from the original one you can create a tokenFamily property in your database model and check for unregistered descendants.
I did not go into full details on how I implemented the whole authentication process in this article, but if you want you can check the source code in the [project's repository in GitHub](https://github.com/alvaromrveiga/ecommerce-backend)