Closed25

学びメモ 2025/01

ebi_yuebi_yu

keycloak SPI

  • Service Provider Interface
  • keycloakの機能を拡張したい/カスタム機能を作りたい時に使用する
  • SPI実装にはProviderProviderFactoryを実装する必要がある
1. Provider インターフェース
実際の機能やロジックを提供するクラスの基盤となるインターフェース。
2. ProviderFactory インターフェース
Provider を生成および管理するための工場 (Factory) クラスの基盤となるインターフェース。

https://keycloak-documentation.openstandia.jp/22.0/ja_JP/server_development/index.html#_providers

Authenticator SPI

Keycloakの認証フローの動作をカスタマイズするためのSPI

https://www.keycloak.org/docs/latest/server_development/index.html#_auth_spi

Authenticator SPIの構成

認証SPIを実装するには、以下のインターフェイスやクラスを実装します。

  1. Authenticator: 認証ロジックの実装。
  2. AuthenticatorFactory: 認証器を生成するファクトリクラス。
  3. ProviderConfigProperty: 設定項目を定義。
  4. KeycloakSessionAuthenticationFlowContext: 認証のために必要な状態やセッション情報の管理。

Authenticator SPIの実装

Authenticator の実装

Authenticator インターフェースは、認証フロー内でユーザーの認証を行うための処理を実装します。

import org.keycloak.authentication.AuthenticationFlowContext;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorConfigModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.UserModel;

public class MyAuthenticator implements Authenticator {

    @Override
    public void authenticate(AuthenticationFlowContext context) {
        // ユーザー認証のロジックを実装
        UserModel user = context.getUser();
        if (user != null && isUserValid(user)) {
            // 認証成功
            context.success();
        } else {
            // 認証失敗
            context.failure(AuthenticationFlowError.INVALID_CREDENTIALS);
        }
    }

    @Override
    public void action(AuthenticationFlowContext context) {
        // 追加のアクションを実装(例: ユーザー確認のための追加処理)
        context.success();
    }

    @Override
    public boolean requiresUser() {
        return true; // ユーザーが認証されている必要がある
    }

    @Override
    public boolean configuredFor(AuthenticationFlowContext context) {
        // 必要な設定がされているかを確認
        AuthenticatorConfigModel config = context.getAuthenticatorConfig();
        return config != null && config.getConfig().containsKey("my_custom_setting");
    }

    @Override
    public void close() {
        // リソース解放
    }

    private boolean isUserValid(UserModel user) {
        // ユーザーの検証ロジック(例: ユーザーが特定の条件を満たすかを確認)
        return true; // サンプルとして常に認証成功
    }
}
  • authenticate(): 認証のメインロジック。ユーザーが有効であれば context.success() を呼び出して認証を成功させます。無効であれば context.failure() を呼び出します。
  • action(): 追加アクション。認証後にさらに処理が必要であればここに記述します。
  • requiresUser(): 認証がユーザー情報に依存するかどうかを返します。ユーザーが必要な場合は true を返します。
  • configuredFor(): 認証のために必要な設定がされているかどうかを確認します。設定が正しくない場合、設定の再確認を促すことができます。

AuthenticatorFactory の実装

AuthenticatorFactory は、Authenticator を生成するためのファクトリクラスです。

import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.utils.DefaultKeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;

import java.util.ArrayList;
import java.util.List;

public class MyAuthenticatorFactory implements AuthenticatorFactory {

    private static final String ID = "my-authenticator";

    @Override
    public String getId() {
        return ID; // 認証フローで識別されるID
    }

    @Override
    public Authenticator create(KeycloakSession session) {
        return new MyAuthenticator(); // MyAuthenticator のインスタンスを作成
    }

    @Override
    public void init(Config.Scope config) {
        // 初期設定を行う
    }

    @Override
    public void postInit(KeycloakSessionFactory factory) {
        // セッションファクトリの初期化後に呼ばれる
    }

    @Override
    public void close() {
        // リソースの解放
    }

    @Override
    public List<ProviderConfigProperty> getConfigProperties() {
        // 設定項目をリストで返す
        List<ProviderConfigProperty> configProperties = new ArrayList<>();
        ProviderConfigProperty property = new ProviderConfigProperty();
        property.setName("my_custom_setting");
        property.setLabel("Custom Setting");
        property.setType(ProviderConfigProperty.STRING_TYPE);
        property.setHelpText("This is a custom setting for the authenticator.");
        configProperties.add(property);
        return configProperties;
    }
}
  • getId(): この認証器の一意なIDを返します。これは認証フロー設定で使用されます。
  • create(): MyAuthenticator のインスタンスを作成して返します。
  • init(): 初期設定を行うために使用されます。設定ファイルや環境変数から情報を取得できます。
  • getConfigProperties(): この認証器に関連する設定項目を返します。ProviderConfigProperty はKeycloakが管理する設定の項目です。ここではカスタム設定を定義しています。

SPIの統合

最後に、META-INF/services/org.keycloak.authentication.AuthenticatorFactory ファイルに AuthenticatorFactory のクラス名を記述し、Keycloakに認証SPIを認識させます。

com.example.keycloak.authenticator.MyAuthenticatorFactory

KeycloakSessionAuthenticationFlowContext の使用

  • KeycloakSession は、ユーザーやセッション情報を操作するために使用します。MyAuthenticatorauthenticate() メソッド内でセッション情報にアクセスすることができます。

  • AuthenticationFlowContext は、現在の認証フローの状態を保持し、認証結果やエラーを通知します。例えば、context.success()context.failure() を呼び出すことで、認証の結果を反映します。

ebi_yuebi_yu

Mavenのmvn installコマンドとローカルリポジトリについて

Mavenでmvn installコマンドを実行すると、ビルドされたアーティファクト(通常は.jarファイルなど)が ローカルリポジトリ にインストールされます。ローカルリポジトリは、通常ユーザーのホームディレクトリ内にある .m2 フォルダに格納されます。

1. 依存関係の解決

mvn installは、プロジェクトの依存関係をリモートリポジトリ(Maven Centralやその他のリポジトリ)からダウンロードし、ローカルリポジトリ(~/.m2/repository)にキャッシュします。

2. ビルドとインストール

ビルドが完了した後、生成されたアーティファクト(例:my-app-1.0.0.jar)は、ローカルリポジトリにインストールされます。インストールされる場所は、~/.m2/repository/ 内の適切なディレクトリに基づいており、次のようなパスで格納されます:

~/.m2/repository/com/example/my-app/1.0.0/my-app-1.0.0.jar

まとめ

  • mvn install はプロジェクトをビルドし、生成されたアーティファクトをローカルリポジトリにインストールします。
  • 依存関係がリモートリポジトリからローカルリポジトリにダウンロードされ、キャッシュされます。
  • インストールされたアーティファクトは、他のプロジェクトでも再利用可能です。
ebi_yuebi_yu

Jest.mockでのCannot access '******' before initialization回避

エラー原因と解決方法

  • エラー原因
    初期化前の testToken を参照してしまう。

  • 解決方法
    遅延評価を利用し、以下のように修正する。

jest.mock('jwt-decode', () => {
    return {
        // ダメな例 :  jwtDecode: jest.fn().mockReturnValue(tetstToken)
        jwtDecode: jest.fn(() => testToken), // 呼び出し時に testToken を評価
    };
});
  • ポイント
    • mockReturnValue は即時評価。
    • jest.fn(() => testToken) は関数実行時に評価されるため、初期化順序の問題を回避できる。
ebi_yuebi_yu

フロントエンドJSから参照できないヘッダーもある

  • 基本的にAccess-Control-Expose-Headersレスポンスヘッダーに設定がないヘッダーはブラウザで実行されるJSからは参照できない
  • CORSポリシーによって制限されている
  • 以下のヘッダーはデフォルトで公開されている
    • Cache-Control
    • Content-Language
    • Content-Length
    • Content-Type
    • Expires
    • Last-Modified
    • Pragma
  • Access-Control-Expose-Headersレスポンスヘッダーへの設定方法は以下
Access-Control-Expose-Headers: <header-name>, <header-name>, ...
Access-Control-Expose-Headers: *

https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Access-Control-Expose-Headers

ebi_yuebi_yu

CORS(Cross-Origin Resource Sharing)の歴史と仕組み

CORS(Cross-Origin Resource Sharing)は、Webブラウザのセキュリティ機構である同一生成元ポリシー(Same-Origin Policy)を緩和し、異なるドメイン間でリソースを共有するための仕組みです。

CORSとは

CORSは、「Webページで実行されているスクリプトが、そのページとは異なるオリジン(ドメイン、プロトコル、ポートの組み合わせ)にあるリソースにアクセスすることを許可するための仕組み」です。具体的には、HTTPヘッダーを用いて、サーバー側がどのドメインからのアクセスを許可するかを指定します。


mbn web docsより引用

  • Origin : どこドメインからのリクエストなのか指定
  • Access-Conrtol-Allow-Origin : リソースがどのドメインからアクセスできるの指定。「*」の場合、全て許可する

なぜ必要になったのか

Webの初期においては、異なるオリジン間のリソース共有はあまり問題になりませんでした。しかし、Webアプリケーションの高度化に伴い、複数のドメインにまたがるサービスやAPIの利用が増加しました。このような状況で、同一生成元ポリシーが厳格に適用されると、正当なリクエストもブロックされてしまい、Webアプリケーションの利便性が損なわれます。そこで、安全性を確保しつつ異なるオリジン間のリソース共有を可能にするために、CORSが必要となりました。

CORSの歴史

時期 出来事 詳細
1995年 同一生成元ポリシーの導入 Netscape Navigator 2.02でJavaScriptが導入された直後に、悪意のあるWebサイトが他のWebサイトのデータを不正に取得するのを防ぐ目的で導入。
Webアプリ進化後 異なるオリジン間のリソース共有の必要性の高まり Webアプリケーションの高度化に伴い、複数のドメインにまたがるサービスやAPIの利用が増加。同一生成元ポリシーが厳格に適用されると、正当なリクエストもブロックされてしまい、Webアプリケーションの利便性が損なわれる状況が発生。
初期の解決策 JSONP(JSON with Padding)などのハック的な手法 異なるオリジン間のリソース共有を実現するための初期の解決策としてJSONPが用いられた。しかし、JSONPはGETリクエストしか使用できない、エラーハンドリングが難しい、セキュリティ上の脆弱性(例えば、クロスサイトスクリプティング(XSS)攻撃のリスク)があるなどの問題点や制約が存在した。
2000年代後半 W3CによるCORSの標準化 W3C(World Wide Web Consortium)によってCORSの標準化が進められ、HTML5の一部として仕様が定められた。
現在 CORSの普及 CORSの標準化により、より安全で標準的な方法で異なるオリジン間のリソース共有が可能になった。現在のWebアプリケーション開発において、異なるオリジン間のAPI連携などで広く利用されている。

CORSが許可されていない場合どうなる

CORSが許可されていない場合、同一生成元ポリシーによって、異なるオリジンからのリクエストはブラウザによってブロックされます。例えば、http://example.com のWebページから https://api.example.net のAPIにXMLHttpRequestでリクエストを送信しようとすると、CORSがない場合、ブラウザはリクエストを送信しますが、レスポンスを受け取ってもスクリプトからはアクセスできず、コンソールにエラーを表示します。

エラーメッセージ

No 'Access-Control-Allow-Origin' header is present on the requested resource.

CORSリクエストの例

CORSが適用されるリクエスト(異なるオリジンからのリクエストが許可されるリクエスト)には
以下の3つのパターンがあります

  • プリフライトリクエスト
  • 単純リクエスト
  • 資格情報を含むリクエスト

プリフライトリクエスト

リクエスト初めにOPTIONメソッドによるHTTPリクエスト(プリフライトリクエスト)を送り、実際のリクエストを送っても安全かを確認します。
リクエストがユーザーデータに影響を与える可能性があるような場合に行われます。

プリフライトリクエストでは「実際のリクエストのメソッド」、「実際のリクエストで送りたいヘッダー」を送信し、
リスポンスでは、「有効なメソッド」、「有効なヘッダー」、「有効なオリジン)」、「有効なキャッシュ時間」を返します。

プリフライトリクエストが完了したら、実際のリクエストを送ります。

# プリフライトリクエストヘッダー
Access-Control-Request-Method: POST // 実際のリクエストのメソッド
Access-Control-Request-Headers: content-type,x-pingother // 実際のリクエストで送りたいヘッダー

3 プリフライトリスポンスヘッダー
Access-Control-Allow-Origin: https://foo.example  // 有効なオリジン
Access-Control-Allow-Methods: POST, GET, OPTIONS  / 有効なメソッド
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type // 有効なヘッダー
Access-Control-Max-Age: 86400 // 有効なキャッシュ時間

https://developer.mozilla.org/ja/docs/Web/HTTP/CORS#プリフライトリクエスト

単純リクエスト

以下の条件を満たす場合はプリフライトリクエストが発生しません。
その場合、ブラウザはレスポンスの Access-Control-Allow-Originヘッダーを確認し、許可されたオリジンであればレスポンスをスクリプトが利用できます。。


mbn web docsより引用

単純リクエストの条件

条件 詳細
メソッド GET、HEAD、POST のいずれか
リクエストヘッダー 以下のヘッダー以外を含まないこと(ユーザーエージェントによって自動的に設定されるヘッダーは除く)。
- Accept
- Accept-Language
- Content-Language
- Content-Type
- DPR
- Downlink
- Save-Data
- Viewport-Width
- Width
Content-Typeヘッダーの値 以下のいずれかであること。
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain

資格情報を含むリクエスト

資格情報を含むリクエストを行う場合、クライアント側でcredentialsオプションをincludeに設定する必要があります。また、サーバー側はプリフライトリクエストと実際のリクエストの両方のレスポンスにAccess-Control-Allow-Credentials: trueを設定する必要があります

const url = "https://bar.other/resources/credentialed-content/";

const request = new Request(url, { credentials: "include" });

const fetchPromise = fetch(request);
fetchPromise.then((response) => console.log(response));
# プリフライトリスポンスヘッダー
Access-Control-Allow-Credentials: true

# リクエストヘッダー
Cookie: pageAccess=2

# リスポンスヘッダー
Access-Control-Allow-Credentials: true


mbn web docsより引用

まとめ

  • CORSは、「Webページで実行されているスクリプトが、そのページとは異なるオリジン(ドメイン、プロトコル、ポートの組み合わせ)にあるリソースにアクセスすることを許可するための仕組み」
  • CORSがない場合、同一生成元ポリシーによって、異なるオリジンからのリクエストはブラウザによってブロックされる。
  • CORSが適用されるパターンにはプリフライトリクエスト単純リクエスト 資格情報を含むリクエストがある
ebi_yuebi_yu

Figma コード生成

ツール名 料金 デザイン再現度 コード生成言語 フレームワーク対応 その他機能
CodeGenerator 無料 React × カスタムプロパティ対応
anima 有料(無料枠あり) React,Vue, HTML/CSS アニメーション、インタラクション
Builder.io 有料(無料枠あり) React, HTML/CSS CMS機能、A/Bテスト
Locofy.ai 有料(無料枠あり) React, HTML/CSS Eコマース機能、SEO最適化
ebi_yuebi_yu

色々触ってみたが微妙。

figmaの画像をスクショ + cluad 3.5で生成が一番良いかも。

ebi_yuebi_yu

BitnamiのKeycloak Dockerイメージへのコンテナアクセス時には非特権ユーザがデフォルトで使われる

BitnamiのKeycloak Dockerイメージ(bitnami/keycloak:24.0.2)では、デフォルトでrootユーザーのパスワードは設定されていません。通常、非特権ユーザー(ID: 1001)が使用されます。

docker execでコンテナにアクセスする際、rootユーザーのパスワードは不要で、以下のコマンドでrootとして入ることができます:

docker exec -it --user root <container_name> bash
ebi_yuebi_yu

Error:caseで元エラーをErrorオブジェクトに含める

  • Errorオブジェクトのcaseに元のエラーを含める
  • ネストされたtry catchでも機能する

コード例

try {
  connectToDatabase();
} catch (err) {
  throw new Error("データベースへの接続に失敗しました。", { cause: err });
}

// 入れ子構造になっていても全てのcaseを保持
try {
  try {
    connectToDatabase();
  } catch (err) {
    throw new Error("データベースへの接続に失敗しました。", { cause: err });
  }
} catch (err) {
  throw new Error("hogeError", { cause: err });
}

throwされるErrorの構造例

{
  name: "Error",
  message: "hogeError",
  cause: {
    name: "Error",
    message: "データベースへの接続に失敗しました。",
    cause: {
      name: "Error",
      message: "オリジナルの接続エラー"
    }
  }
}

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Error/cause

ebi_yuebi_yu

AIでコード開発からデプロイまでの動化

  1. felo: 要件定義書を作成
  2. v0: UIデザインを作成。
  3. ChatGPT: 画面設計書を作成。
  4. Replit Agent: [設計書、定義書、UIデザインをもとにコード開発を自動化]

参考リンク

https://qiita.com/ryosuke_ohori/items/c40d357bdab6e6cb1c56

https://zenn.dev/masa_oka108/articles/92669dbfc3bfd9

考察

  • 実際のフローはこれだけで完結することが可能なアプリが限定される。
  • コンポーネントのコントロール性やデプロイ対策に不安な点も見られる。
  • コンポーネントの質、安全性、活用性については今後の調査が必要。
ebi_yuebi_yu

pgvector

  • PostgresSQLの拡張機能
  • PostgresSQLをベクトルデータベースとしてとして使うためのもの
  • ベクトルデータの検索方法には以下がある
    • L2 距離(ユークリッド距離)
    • 内積
    • コサイン類似度(またはコサイン距離)

https://github.com/pgvector/pgvector

1. L2距離(ユークリッド距離)

概要:

2つのベクトルの「直線距離」を計算します。ベクトルがどれくらい離れているかを測る指標です。

https://taketake2.com/N102.html

特徴:

  • 距離が小さいほどベクトルが近い(似ている)。
  • スケールの影響を受けやすい(値が大きいと影響が増す)。

2. 内積

概要

2つのベクトルの「方向と大きさ」を掛け合わせた値です。内積が大きいほど、ベクトルが「似た方向」を向いていることを示します。

https://taketake2.com/N43.html

特徴

  • 値が正のとき、2つのベクトルは同じ方向。
  • 値が負のとき、2つのベクトルは反対方向。
  • スケール(ベクトルの長さ)に影響を受ける。

3. コサイン類似度(コサイン距離)

概要

2つのベクトルの「角度」に基づいて類似度を測ります。大きさの影響を受けないため、方向の比較に適しています。

https://taketake2.com/N103.html

特徴

  • 1(完全一致)から-1(完全反対)までの値を取る。
  • ベクトルの大きさ(スケール)の影響を受けない。
  • コサイン類似度が高いほど、2つのベクトルは同じ方向を向いている。

用途

AI用ベクトルデータベースにおける指標選択の基準

AI用のベクトルデータベースを利用する際には、データの特性や目的に応じて適切な指標を選択することが重要です。以下に、各指標を選択する際の基準をまとめます。

1. L2距離

  • 用途: 主にベクトル間の実際の距離を測定するために使用。
  • 基準:
    • ベクトルのスケールが統一されている場合に適しています。
    • 物理的な距離や誤差を評価する際に有効です。
    • スケールの違いが結果に影響を与えない状況で使用。

2. 内積

  • 用途: ベクトルの方向性と大きさを同時に評価。
  • 基準:
    • ベクトルの大きさが結果に重要な影響を与える場合に適しています。
    • 同じ方向を向いているベクトルを強調したい場合に使用。
    • 大きな値が重要な意味を持つ評価に適しています。

3. コサイン類似度

  • 用途: ベクトルの方向性のみを評価。
  • 基準:
    • ベクトルの大きさを無視し、方向性の類似度を重視する場合に適しています。
    • スケールが異なるベクトル間の比較に最適です。
    • ベクトルの角度の一致度を評価したい場合に使用。

選択のポイント

  • データのスケール: データのスケールが異なる場合は、コサイン類似度を選択することでスケールの影響を排除できます。
  • 評価の目的: 距離を測定したいのか、方向性を評価したいのかによって指標を選択します。
  • 計算コスト: 内積やコサイン類似度は計算が比較的軽量であるため、大規模データセットにおいても効率的に使用できます。

これらの基準を考慮し、目的に応じた指標を選択することで、AI用ベクトルデータベースを効果的に活用できます。

参考

https://qiita.com/hmatsu47/items/b393cecef8ed9df57c35#テーブル作成データ投入

https://taketake2.com

ebi_yuebi_yu

Nest.jsでのルーディングについて

  • 標準パターン(勝手にリスポンスをJSONにして返してくれる)とカスタマイズパターンがある
  • カスタマイズパターンの場合は@Res resをつける
// 標準パターン
@Post()
create() {
  return { "message" : "test" };
}
/* リスポンスは以下
 {
  "message": "test"
}
*/

// カスタマイズパターン
@Post()
@HttpCode(204)
create(@Res() res: Response) {
  if (...) {
    res.status(500).send('Error!')
  }
  return { "message" : "test" };
}

https://docs.nestjs.com/controllers#routing

ebi_yuebi_yu

Nest.jsメモ

controller

リクエスト

requestオブジェクトにアクセスするためのデコレーター一覧

https://docs.nestjs.com/controllers#request-object

リソース

  • @Put()@Delete()@Patch()@Options()@Head()@All()
  • @All() デコレータを使うことでこれら全ての HTTP リクエストメソッドを扱うエンドポイントを定義できる。

https://docs.nestjs.com/controllers#resources

Sub-Domain Routing

  • @Controller デコレータの host オプションにホスト名を指定することで、HTTPリクエストを行うホスト名のマッチングを行うことができる。

https://docs.nestjs.com/controllers#sub-domain-routing

ebi_yuebi_yu

git エラーログ表示

# トレースログ表示
set GIT_TRACE=1
# Gitのhttpリクエストのログを表示
set GIT_CURL_VERBOSE=1
  • GIT_TRACE=1: Git のコマンド実行時に、詳細なトレースログを表示します。このログには、Git が実行している内部処理や、どのようなコマンドが発行されたか、どのリソースが呼び出されたかが記録されます。
  • GIT_CURL_VERBOSE=1: Git が HTTP リクエストを送信する際、curl の詳細なデバッグ情報を表示します。接続エラーや認証に関連する問題を調査する際に有用です。

詳しくは下記リンクのデバッグ項、ネットワーク項を参照

https://git-scm.com/book/ja/v2/Gitの内側-環境変数

ebi_yuebi_yu

uuidv4/uuidv7/ulid

uuidv4

  • ランダムに生成される128ビットの値
  • 完全にランダム(時系列準でのソートなどはできない)
  • 生成が非常に簡単で、高い一意性を持つ
8ca5536e-bb95-e070-8527-8d08d051f875

uuidv7

  • uuidv4を時系列準でソート可能にしたもの
  • 最初の48ビットがタイムスタンプとしてエンコードされており、あとはランダム
  • 懸念として生成時刻を容易に特定することが可能な点がある
// 8ca5536e-bb95までタイムスタンプ部分
8ca5536e-bb95-e070-8527-8d08d051f875

ulid

  • こちらもソートが可能な一意なID
  • 最初の48ビットがタイムスタンプとしてエンコードされており、あとはランダム
  • 形式がuuidと違う
  • 懸念として生成時刻を容易に特定することが可能な点がある
// 01HJA3X5YKが タイムスタンプ部分
01HJA3X5YK2V6XQJRTMZ2F4GNP

参考

https://zenn.dev/kazu1/articles/e8a668d1d27d6b#まとめのようなもの

ebi_yuebi_yu

ワンタイムパスワード(OTP)

ワンタイムパスワード(OTP)は、一度限り有効な使い捨てパスワードです。
通常のパスワードと異なり、動的に生成されるため、パスワードの使い回しや漏洩による不正アクセスを防ぐ効果があります。
二要素認証(2FA)や多要素認証(MFA)の重要な要素として広く利用されています。
OTPの生成には、共通のシークレットキーと時間(TOTP)またはカウンター(HOTP)を用い、ハッシュ関数を適用することで認証を実現します。

OTPの登録フロー

  1. OTPの有効化:
    • ユーザーはサービスの設定画面などでOTPを有効化します。
  2. シークレットキーの生成とQRコード表示:
    • サーバーはシークレットキーを生成し、QRコードとしてユーザーに提示します。
  3. OTPアプリへの登録:
    • ユーザーはスマートフォンなどのOTPアプリ(例:Google Authenticator、Microsoft Authenticator)でQRコードを読み取ります。これにより、アプリにシークレットキーが保存されます。
  4. OTPの確認:
    • アプリに表示されたOTPコードをサービスに入力し、登録を完了します。

OTPの入力フロー

OTPの検証フロー

  1. ログイン画面で認証
    • ユーザーはIDとパスワードを入力します。
  2. OTP入力要求
    • サービスはOTPの入力を要求します。
  3. OTPアプリでコード確認
    • ユーザーはOTPアプリで現在のOTPコードを確認します。
  4. OTPコードの入力
    • ユーザーはOTPコードをサービスに入力します。
  5. サーバー側でOTPの検証を実施
    • 現在のタイムスタンプを取得
    • シークレットキーとタイムスタンプから期待されるOTPを生成
    • 入力されたOTPと比較**
      • ±1スロット(±30秒)の時間範囲内でOTPが一致するかチェック
      • 一致すれば認証成功
  6. 認証成功/失敗の判定
    • OTPが正しければ認証成功し、ログインを許可
    • OTPが間違っている、または時間範囲外なら認証失敗
  7. ログイン処理の継続またはエラー通知
    • 認証成功 → ユーザーはログイン成功し、サービスを利用可能
    • 認証失敗 → ユーザーにエラー通知(「OTPコードが無効です」など)

補足

  • 時刻同期が重要
    サーバーの時刻とユーザーの端末の時刻がズレていると認証失敗するため、サーバーはNTPを使って時刻を同期する。

  • タイムウィンドウの許容範囲
    ±1スロット(通常±30秒)を許容することで、多少の時間ズレに対応可能。

  • 連続失敗対策
    短時間に複数回OTPを間違えた場合、アカウントロックや追加認証を要求する対策を実装するとセキュリティが強化される。

    OTPの仕組み:TOTPとHOTP

1. TOTP(Time-based One-Time Password:時間ベースワンタイムパスワード)

TOTPは、時刻同期に基づいて生成されるOTPです。


https://www.onelogin.com/jp-ja/learn/otp-totp-hotp

  • 仕組み:

    1. シークレットキーの生成: ユーザー登録時にサーバーとユーザーの間で共有される秘密鍵(シークレットキー)が生成されます。
    2. タイムスタンプの利用: 現在時刻を特定の時間間隔(通常は30秒または60秒)で区切った値(タイムスタンプ)を使用します。
    3. HMAC-SHA1などのハッシュ関数: シークレットキーとタイムスタンプを組み合わせ、HMAC-SHA1などのハッシュ関数を用いてOTPを生成します。
  • 特徴:

    • 時間同期が必須:サーバーとユーザーの時刻が同期している必要があります。
    • 短時間有効:生成されたOTPは短い時間(例:30秒)のみ有効です。

2. HOTP(HMAC-based One-Time Password:HMACベースワンタイムパスワード)

HOTPは、カウンター値に基づいて生成されるOTPです。


https://www.onelogin.com/jp-ja/learn/otp-totp-hotp

  • 仕組み:

    1. シークレットキーとカウンター: ユーザー登録時にサーバーとユーザーの間で共有されるシークレットキーと、カウンターと呼ばれる数値を使用します。
    2. カウンターのインクリメント: OTPが生成されるたびにカウンターがインクリメントされます。
    3. HMAC-SHA1などのハッシュ関数: シークレットキーとカウンター値を組み合わせ、HMAC-SHA1などのハッシュ関数を用いてOTPを生成します。
  • 特徴:

    • 時間同期不要:時刻に依存しないため、時刻同期は不要です。
    • カウンターの同期が重要:サーバーとユーザーのカウンター値が一致している必要があります。

OTPの挙動と注意点

複数端末での利用

同じQRコードを複数の端末でスキャンした場合、各端末で同じOTPコードが生成されます。これは利便性がある反面、セキュリティリスクも伴います。端末の紛失・盗難時には、不正利用される可能性があります。

シークレットキーの再登録

OTPをリセットする場合、新しいシークレットキーが生成され、以前の端末ではOTPが無効になります。

OTPコードの有効期限

OTPコードは通常、短い有効期限(例:30秒)が設定されています。

KeycloakでのOTPの例

Keycloakは、TOTPを用いたOTP認証をサポートしています。
ユーザーはKeycloakの管理コンソールでTOTPを有効化し、QRコードをOTPアプリで読み取ることでOTPを設定できます。
Keycloakは内部で時刻同期を行っているため、ユーザーはどの端末からでもOTPを生成できます。

まとめ

OTPは、オンラインサービスのセキュリティを強化するための有効な手段です。
特にTOTPは、時間同期に基づいて生成されるため、高いセキュリティを提供します。Keycloakなどの認証基盤を利用することで、簡単にOTPを導入できます。
ただし、複数端末での利用など、セキュリティリスクについても理解しておくことが重要です。

参考

https://www.onelogin.com/jp-ja/learn/otp-totp-hotp

ebi_yuebi_yu

テスト

-------開発側-------
- 単体テスト : ユニットテスト 
- ita (内部結合) : 単一システムの結合
- itb (外部結合) : 複数システムの結合
- システムテスト : パフォーマンス/負荷検証、非機能、全体の結合、
-------ユーザー側-------
- 受け入れテスト 
- UAT(ユーザーテスト)
ebi_yuebi_yu

jestのtest.eachの引数に、beforeEach/beforeAllで値を設定することはできない。

  • test.eachを使っている場合、beforeEachやbeforeAllで設定した変数をtestの引数として指定すると、undefined(未定義)になる
  • jestのテストデータの収集が動機的に行われるのが原因らしい
let a;
beforeEach(() => {
   a = 'hello';
})

describe('my test suite', () => {
    test.each([
        [a, 'hello']
    ])(
    'testing %s with expected result %s',
    (myVariable, expectedResult) => {
   // myVariableはundefinedになる
        expect(myVariable).toBe(expectedResult);
    })
});

https://git.cicd.lakeelcloud.com/applications/services/lakeel-visual-mosaic/sample/part-visual-mosaic-samples

https://jestjs.io/docs/troubleshooting#defining-tests

このスクラップは2025/02/04にクローズされました