Auth0のSDKを使ってNestJSからユーザーを登録する方法

# はじめに

`Auth0`のSDKを利用して`Auth0 Management API`でユーザーをAUth0のDBに登録する方法についてまとめます。

参考資料はこちら

https://dev.classmethod.jp/articles/manage-auth0-users-using-auth0-management-api-with-nodejs/

## 必要なモジュールをインストール

https://www.npmjs.com/package/auth0

https://www.npmjs.com/package/@types/auth0

## `Auth0`のモジュールを定義する

環境変数については[Configuration](https://docs.nestjs.com/techniques/configuration)を利用して読み込んでいます

`Auth0`の`ManagementClient`をインスタンス化するための`domain`・`clientId`・`clientSecret`については冒頭の参考資料に細かく説明されているのでそちらを参照ください

環境変数については本番環境・検証環境ごとに`Cloud Run`に設定した環境変数をもとにプロセスから読み込むことを想定しています

https://cloud.google.com/run/docs/configuring/environment-variables?hl=ja

:::details モジュールクラス

```ts:app/src/common/auth0/auth0.module.ts
import { Module } from '@nestjs/common'
import { LoggerModule } from 'nestjs-pino'
import { ConfigModule } from '@nestjs/config'
import { Auth0Service } from './auth0.service'

@Module({
  providers: [Auth0Service],
  exports: [Auth0Service],
  imports: [LoggerModule, ConfigModule],
})
export class Auth0Module {}
```
:::

:::details サービスクラス
```ts:app/src/common/auth0/auth0.service.ts
import { Injectable, InternalServerErrorException } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import { ManagementClient } from 'auth0'
import { CreateAuth0UserInput } from '../../types/auth0/CreateAuth0UserInput'
import { CreateAuth0UserOutput } from '../../types/auth0/CreateAuth0UserOutput'

@Injectable()
export class Auth0Service {
  private readonly auth0Client: ManagementClient
  private readonly AUTH0_CONNECTION = 'Username-Password-Authentication'

  constructor(private configService: ConfigService) {
    this.auth0Client = new ManagementClient({
      domain: this.configService.get('auth0.domain'),
      clientId: this.configService.get('auth0.clientId'),
      clientSecret: this.configService.get('auth0.clientSecret'),
      scope: 'create:users read:users update:users',
    })
  }

  async createUserByAuth0({
    email,
    password,
  }: CreateAuth0UserInput): Promise<CreateAuth0UserOutput> {
    try {
      const { user_id } = await this.auth0Client.createUser({
        connection: this.AUTH0_CONNECTION,
        email: email,
        password: password,
      })

      return {
        user_id: user_id,
      }
    } catch (e: unknown) {
      if (e instanceof Error) {
        throw new InternalServerErrorException(e.message)
      }
      
      throw new InternalServerErrorException(e)
    }
  }
}
```
:::

## `Resolver`を定義する

今回は`GraphQL`を利用するため`Resolver`を定義していますが、`RestAPI`で実装されている場合はこちらの手順は不要になります。
使用するモジュールで先ほど定義した`Auth0`を操作するモジュールを依存注入してご利用ください。


:::details InputType
```ts:app/src/types/auth0/CreateAuth0UserInput.ts
import { Field, InputType } from '@nestjs/graphql'

@InputType()
export class CreateAuth0UserInput {
  @Field()
  email: string

  @Field()
  password: string
}
```
:::

:::details ObjectType
```ts:app/src/types/auth0/CreateAuth0UserOutput.ts
import { Field, ObjectType } from '@nestjs/graphql'

@ObjectType()
export class CreateAuth0UserOutput {
  @Field()
  user_id: string
}
```
:::

:::details リゾルバクラス
```ts:app/src/author/author.resolver.ts
import { Args, Int, Mutation, Query, Resolver } from '@nestjs/graphql'
import { AuthorService } from './author.service'
import { Author } from './author.model'
import { CreateAuth0UserInput } from '../types/auth0/CreateAuth0UserInput'
import { Auth0Service } from '../common/auth0/auth0.service'
import { CreateAuth0UserOutput } from '../types/auth0/CreateAuth0UserOutput'

@Resolver(() => Author)
export class AuthorsResolver {
  constructor(
    private readonly authorsService: AuthorService,
    private readonly auth0Service: Auth0Service,
  ) {}

  @Query(() => String)
  async author() {
    return 'hoge'
  }

  @Mutation((returns) => CreateAuth0UserOutput)
  async saveUserByAuth0(
    @Args('auth0Data') auth0InputData: CreateAuth0UserInput,
  ) {
    return this.auth0Service.createUserByAuth0(auth0InputData)
  }
}
```
:::

:::details モジュールクラス
```ts:app/src/author/author.module.ts
import { Module } from '@nestjs/common'
import { AuthorService } from './author.service'
import { AuthorsResolver } from './author.resolver'
import { Auth0Module } from '../common/auth0/auth0.module'

@Module({
  imports: [Auth0Module],
  providers: [AuthorsResolver, AuthorService],
})
export class AuthorModule {}
```
:::

## 実行してみる

成功すると`Autho`で発行されたIDが返却されます

```graphql
mutation MyMutation {
  saveUserByAuth0(auth0Data: {email: "hoge8@example.com", password: "Password1234"}) {
    user_id
  }
}
```