メインコンテンツまでスキップ

多要素認証の実装

サンプルアプリのMFA設定機能を題材に、SaaSus Auth APIを使用して多要素認証(MFA)機能を実装する方法を解説します。

サンプルアプリでは、以下の2つの認証方式を選択できます。

  • 認証アプリ(TOTP): Google AuthenticatorやAuthyなどの認証アプリで生成されるワンタイムコードを使用
  • メール認証: ログイン時に登録メールアドレスへ認証コードを送信
メール認証方式を利用する場合の注意

多要素認証(MFA)の方式として「メール」を使用しているユーザーは、SaaSus Platform が生成するログイン画面からパスワードをリセットすることはできません。
このようなユーザーのパスワードを再設定する場合は、SaaS 運用コンソールのユーザー管理画面から、管理者によるパスワードリセットを行ってください。

以下は多要素認証設定ダイアログのスクリーンショットです。

MFA機能では以下の操作を提供します。

  • MFA設定状態と認証方式の確認
  • 認証方式の選択(認証アプリ / メール認証)
  • 認証アプリケーション(Google Authenticator等)の登録
  • MFAの有効化・無効化
  • QRコードによる認証アプリのセットアップ
  • 認証方式の切り替え
前提条件

メール認証を利用するには、SaaSus Platform でドメイン名の設定とメール送信ドメイン認証(DKIM)が完了している必要があります。 設定方法は ドメイン・メール送信ドメイン認証・遷移先の設定 を参照してください。

フロントエンド実装

認証方式選択のUIフロー

フロントエンドでは、ステートマシンによりMFA設定ダイアログの表示を管理しています。

  1. ダイアログを開くと GET /mfa_status でMFA状態を取得
  2. 未設定の場合は方式選択画面を表示(認証アプリ / メール認証のカード選択)
  3. 認証アプリを選択した場合はQRコード表示と認証コード入力へ遷移
  4. メール認証を選択した場合は確認画面を表示し、有効化を実行
  5. 設定済みの場合は現在の方式を表示し、別の方式への切り替えや無効化が可能

実装例リンク

以下のリンク先に、フロントエンドの実装が含まれています。

バックエンド実装

エンドポイント一覧

種別メソッド & パス説明
MFA状態確認GET /mfa_statusユーザーのMFA有効/無効状態と認証方式を取得します。
MFAセットアップGET /mfa_setup認証アプリ登録用のQRコードURLを生成します。
MFA認証コード検証POST /mfa_verify認証アプリからの認証コードを検証し、MFAを登録します。
MFA有効化(認証アプリ)POST /mfa_enable認証アプリ方式でMFAを有効化します。
MFA有効化(メール認証)POST /mfa_email_enableメール認証方式でMFAを有効化します。
MFA無効化POST /mfa_disableユーザーのMFAを無効化します。
備考

以下のコードサンプルはバックエンドがGoを前提としています。

MFA状態確認エンドポイント

GET /mfa_status は、MFAの有効/無効状態に加えて、現在設定されている認証方式(softwareToken または email)を返します。

// MFAの状態を取得 (有効/無効の確認と認証方式)
func getMfaStatus(c echo.Context) error {
// コンテキストからユーザー情報を取得
userInfo, ok := c.Get(string(ctxlib.UserInfoKey)).(*authapi.UserInfo)
if !ok {
c.Logger().Error("Failed to get user info")
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to retrieve user information"})
}

// SaaSus の API を使用してユーザーの MFA 設定を取得
response, err := authClient.GetUserMfaPreferenceWithResponse(context.Background(), userInfo.Id)
if err != nil || response.JSON200 == nil {
c.Logger().Errorf("failed to get MFA status: %v", err)
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to retrieve MFA status"})
}

// MFA の有効/無効の状態と認証方式を返す
result := map[string]interface{}{
"enabled": response.JSON200.Enabled,
}
if response.JSON200.Method != nil {
result["method"] = string(*response.JSON200.Method)
}
return c.JSON(http.StatusOK, result)
}

レスポンス例:

{ "enabled": true, "method": "softwareToken" }
{ "enabled": true, "method": "email" }
{ "enabled": false }

実装例リンク

以下のリンク先に、本エンドポイントの実装が含まれています。
関数名で検索して該当箇所をご確認ください。

MFAセットアップエンドポイント

認証アプリ方式でMFAを設定する際に使用します。 QRコードURLを生成し、ユーザーが認証アプリでスキャンしてTOTPデバイスを登録します。

// MFAのセットアップ情報を取得 (QRコードを発行)
// フロントエンドアプリは、リクエストヘッダーに X-Access-Token を含める必要があります
func getMfaSetup(c echo.Context) error {
// リクエストヘッダーから X-Access-Token を取得
accessToken := c.Request().Header.Get("X-Access-Token")
if accessToken == "" {
// アクセストークンがない場合は、認証エラーを返す
return c.JSON(http.StatusUnauthorized, map[string]string{"error": "Access token is missing"})
}

// コンテキストからユーザー情報を取得
userInfo, ok := c.Get(string(ctxlib.UserInfoKey)).(*authapi.UserInfo)
if !ok {
c.Logger().Error("failed to get user info")
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to retrieve user information"})
}

// SaaSus API を使用して 認証アプリケーション登録用のシークレットコードを作成
response, err := authClient.CreateSecretCodeWithResponse(context.Background(), userInfo.Id, authapi.CreateSecretCodeJSONRequestBody{
AccessToken: accessToken,
})
if err != nil || response.JSON201 == nil {
c.Logger().Errorf("failed to create secret code: %v", err)
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to generate QR code"})
}

// Google Authenticator などで使用する QR コード URL を生成
qrCodeUrl := "otpauth://totp/SaaSusPlatform:" + userInfo.Email + "?secret=" + response.JSON201.SecretCode + "&issuer=SaaSusPlatform"

// QR コード URL を返す
return c.JSON(http.StatusOK, map[string]string{
"qrCodeUrl": qrCodeUrl,
})
}

実装例リンク

以下のリンク先に、本エンドポイントの実装が含まれています。
関数名で検索して該当箇所をご確認ください。

MFA認証コード検証エンドポイント

認証アプリで生成された6桁のコードを検証し、TOTPデバイスを登録します。

// ユーザーのMFA認証コードを検証
// フロントエンドアプリは、リクエストヘッダーに X-Access-Token を含める必要があります
func verifyMfa(c echo.Context) error {
// コンテキストからユーザー情報を取得
userInfo, ok := c.Get(string(ctxlib.UserInfoKey)).(*authapi.UserInfo)
if !ok {
c.Logger().Error("Failed to get user info")
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to retrieve user information"})
}

// リクエストヘッダーから X-Access-Token を取得
accessToken := c.Request().Header.Get("X-Access-Token")
if accessToken == "" {
return c.JSON(http.StatusUnauthorized, map[string]string{"error": "Access token is missing"})
}

// リクエストボディから認証コードを取得
var requestBody struct {
VerificationCode string `json:"verification_code"`
}
if err := c.Bind(&requestBody); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid request: malformed JSON or incorrect parameters"})
}
if requestBody.VerificationCode == "" {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Verification code is required"})
}

// SaaSus API を使用して 認証アプリケーションを登録
response, err := authClient.UpdateSoftwareTokenWithResponse(context.Background(), userInfo.Id, authapi.UpdateSoftwareTokenJSONRequestBody{
AccessToken: accessToken,
VerificationCode: requestBody.VerificationCode,
})
if err != nil || response.StatusCode() != http.StatusOK {
c.Logger().Errorf("MFA verification failed: Status Code %d, Response %s", response.StatusCode(), string(response.Body))
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "MFA verification failed"})
}

return c.JSON(http.StatusOK, map[string]string{"message": "MFA verification successful"})
}

実装例リンク

以下のリンク先に、本エンドポイントの実装が含まれています。
関数名で検索して該当箇所をご確認ください。

MFA有効化エンドポイント(認証アプリ)

認証コードの検証が成功した後に呼び出し、認証アプリ方式でMFAを有効化します。

// MFAを有効化する(認証アプリ)
func enableMfa(c echo.Context) error {
// コンテキストからユーザー情報を取得
userInfo, ok := c.Get(string(ctxlib.UserInfoKey)).(*authapi.UserInfo)
if !ok {
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to retrieve user information"})
}

// MFA を認証アプリで有効化するためのリクエストボディを作成
method := authapi.MfaPreferenceMethodSoftwareToken
requestBody := authapi.UpdateUserMfaPreferenceJSONRequestBody{
Enabled: true,
Method: &method,
}

// SaaSus API を使用して MFA を有効化
_, err := authClient.UpdateUserMfaPreferenceWithResponse(context.Background(), userInfo.Id, requestBody)
if err != nil {
c.Logger().Errorf("Failed to enable MFA: %v", err)
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to enable MFA"})
}

return c.JSON(http.StatusOK, map[string]string{"message": "MFA has been enabled"})
}

実装例リンク

以下のリンク先に、本エンドポイントの実装が含まれています。
関数名で検索して該当箇所をご確認ください。

MFA有効化エンドポイント(メール認証)

SDK対応状況

メール認証方式のMFA有効化に対応しているSDKは、現在 GoJavaScript のみです。

メール認証方式でMFAを有効化します。 認証アプリ方式と異なり、デバイス登録(setup/verify)は不要です。 有効化すると、次回ログイン時に登録メールアドレスへ認証コードが送信されます。

// MFAをメール認証で有効化する
func enableMfaEmail(c echo.Context) error {
// コンテキストからユーザー情報を取得
userInfo, ok := c.Get(string(ctxlib.UserInfoKey)).(*authapi.UserInfo)
if !ok {
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to retrieve user information"})
}

// メール認証で MFA を有効化するためのリクエストボディを作成
method := authapi.MfaPreferenceMethodEmail
requestBody := authapi.UpdateUserMfaPreferenceJSONRequestBody{
Enabled: true,
Method: &method,
}

// SaaSus API を使用して MFA をメール認証で有効化
_, err := authClient.UpdateUserMfaPreferenceWithResponse(context.Background(), userInfo.Id, requestBody)
if err != nil {
c.Logger().Errorf("Failed to enable email MFA: %v", err)
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to enable email MFA"})
}

return c.JSON(http.StatusOK, map[string]string{"message": "Email MFA has been enabled"})
}

実装例リンク

以下のリンク先に、本エンドポイントの実装が含まれています。
関数名で検索して該当箇所をご確認ください。

  • Go (Echo): enableMfaEmail
  • Python (FastAPI): SDKのメール認証方式対応後に追加予定
  • Java (Spring): enableMfaEmail(SDKリリース待ちのため feature ブランチで対応)
  • C# (.NET 8): SDKのメール認証方式対応後に追加予定
  • C# (.NET Framework 4.8): SDKのメール認証方式対応後に追加予定

MFA無効化エンドポイント

// MFAを無効化する
func disableMfa(c echo.Context) error {
// コンテキストからユーザー情報を取得
userInfo, ok := c.Get(string(ctxlib.UserInfoKey)).(*authapi.UserInfo)
if !ok {
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to retrieve user information"})
}

// MFA を無効化するためのリクエストボディを作成
method := authapi.MfaPreferenceMethodSoftwareToken
requestBody := authapi.UpdateUserMfaPreferenceJSONRequestBody{
Enabled: false,
Method: &method,
}

// SaaSus API を使用して MFA を無効化
_, err := authClient.UpdateUserMfaPreferenceWithResponse(context.Background(), userInfo.Id, requestBody)
if err != nil {
c.Logger().Errorf("Failed to disable MFA: %v", err)
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to disable MFA"})
}

return c.JSON(http.StatusOK, map[string]string{"message": "MFA has been disabled"})
}

実装例リンク

以下のリンク先に、本エンドポイントの実装が含まれています。
関数名で検索して該当箇所をご確認ください。