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

SaaSアプリケーションへの多要素認証(MFA)の実装サンプルについて説明します。

それぞれのAPIの詳細はAPIドキュメントをご確認ください。

MFAの実装サンプル

1.フロントエンド実装

1.1.追加コンポーネント

MFAの認証設定を管理するために、以下のコンポーネントを追加。

1.1.1.MFA設定ダイアログ

  • Reactの実装サンプル
    → ユーザーがMFAを有効化・無効化し、MFAのステータスを確認するダイアログコンポーネント。

1.1.2.ヘッダーユーザーメニュー

1.2.実装サンプルの使い方

2.バックエンド実装

MFA機能を実装するために、以下のエンドポイントを追加しました。

2.1.追加したエンドポイント

2.1.1.MFA設定取得API

e.GET("/mfa_status", getMfaStatus, authMiddleware)

// 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 の有効/無効の状態を返す
return c.JSON(http.StatusOK, map[string]bool{"enabled": response.JSON200.Enabled})
}

2.1.2.認証アプリケーション登録用のシークレットコード作成API

e.GET("/mfa_setup", getMfaSetup, authMiddleware)

// 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,
})
}

2.1.3.認証アプリケーション登録API

e.POST("/mfa_verify", verifyMfa, authMiddleware)

// ユーザーの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"})
}

2.1.4.MFA設定更新API(有効)

e.POST("/mfa_enable", enableMfa, authMiddleware)

// 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.SoftwareToken
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"})
}

2.1.5.MFA設定更新API(無効)

e.POST("/mfa_disable", disableMfa, authMiddleware)

// 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.SoftwareToken
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"})
}

3.実装サンプルの使い方