APEXでパスキーを使用する実装は以前から公開されていましたが、Anton Schefferさんが使いやすいようにサンプル・アプリにしてくれました。
本記事ではこのサンプル・アプリをインストールして、APEXアプリをパスキーで認証する方法を確認します。
Cleaning up an old laptop, so moving some stuff what still might be useful for others to github. See https://t.co/mr2QzwLSlG #orclAPEX #passkeys #WebAuthn
— Anton Scheffer (@AntonScheffer) August 26, 2025
APEX PasskeysのGitHubリポジトリより、サンプル・アプリのエクスポートをダウンロードします。f101.sqlとしてリポジトリに保存されています。サンプル・アプリはAPEX 22.2で作成されているため、アプリをインストールできるAPEXのバージョンは22.2以降になります。
f101.sqlをダウンロードした後に、これをAPEXのワークスペースにインポートします。パスキーで認証するにはHTTPSとDNSに登録された正式なホスト名が必要なため、Always FreeのAutonomous Databaseまたはorclapex.comに作成したワークスペースにアプリケーションをインストールするとよいでしょう。
アプリケーション・ビルダーを開き、インポートをクリックします。
インポートするファイルとして、先ほどGitHubよりダウンロードしたf101.sqlを選択します。ファイルタイプはデフォルトのアプリケーション、ページまたはコンポーネントのエクスポートとします。
次へ進みます。
自動的に選択されているオプションは変更は不要なので、そのままアプリケーションのインストールをクリックします。
インポートしたアプリケーションの名前はDemo for Passkeysです。
サンプル・アプリDemo for Passkeysがインストールされます。必要なデータベース・オプジェクトはプラグインが必要に応じて作成するため、そのままアプリケーションの実行ができます。
今回は最初にアプリケーションの編集を開き、テーマのリフレッシュを実施します。
ページを構成する要素としては単純なものだけが使用されているため、テーマをリフレッシュしても問題は発生しません。
テーマのリフレッシュをクリックします。
テーマのリフレッシュが完了したら、アプリケーションを実行してパスキーによる認証を確認します。
サンプル・アプリケーションの説明ページが表示されます。
Macintoshで動くかどうかわからない、と記載されていますが、macOSでも動作しました。そのほかに、パスキーを使用する手順が説明されています。
- 最初にスキーム・タイプが公開資格証明(ユーザー名だけで認証するテスト用の認証手段)またはOracle APEXアカウントでユーザー認証をします。
- 1でユーザー認証したデバイスでパスキーを登録します。
- この後から、同じデバイスであればパスキーでユーザー認証できます。
ナビゲーション・メニューから、Registerのページを開きます。まだ、APEXアプリケーションにはサインインしていないため、右上のユーザーはnobodyになっています。
サインイン画面が開きます。
パスキーが未登録の場合は、Opendoor Sign InもしくはAPEX Account Sign Inのどちらかを実施して、アプリケーションにサインインします。
ユーザー名の入力は不要です。
今回はユーザー名を自由に決められるOpendoor Sign Inを実施します。
ユーザー名を入力し、Sign Inをクリックします。本来は初回のサインインでも、ユーザー名やパスワードの入力を要求すべきです。
スキーム・タイプが公開資格証明なので、ユーザー名が何でも、ユーザー認証に成功します。
ページにあるボタンRegisterをクリックすると、パスキーが登録されます。
私のmacOSの環境では、MacbookのTouch IDでパスキーを保存するかどうか、確認されました。
Touch IDでの指紋認証が完了すると、registeredとポップアップが表示されます。
OKをクリックしてポップアップを閉じます。
サインアウト後、再度ナビゲーション・メニューよりRegisterを開きます。
Sign Inをクリックし、パスキーによるサインインを実施します。
生体認証については、デバイスごとに手順は異なるでしょう。
Touch IDによる指紋認証に成功すると、パスキーを登録したユーザーでサインインが完了します。
サンプル・アプリケーションのパスキーによる認証は、以上のように動作します。
macOSのパスワード・アプリを開くと、パスキーが登録されていることが確認できます。
以下より、パスキーによる認証の実装について紹介します。
データベース・オブジェクトとしては表AS_USER_PASSKEYSとパッケージAS_PASSKEYSが作成されます。表AS_USER_PASSKEYSには、APEXのワークスペースID、アプリケーションID、サインインするユーザー名とそのユーザに紐づいたパスキーが保存されます。
表AS_USER_PASSKEYSのDDLは以下です。列PASSKEYSの型はCLOBですが、JSON形式のパスキー(WebAuthn認証情報)が保存されます。
create table as_user_passkeys
( id number generated always as identity constraint as_user_passkeys2_pk primary key
, workspace_id number not null
, app_id number not null
, name varchar2(4000 char) not null
, extra varchar2(4000 char)
, passkeys clob
)
パッケージAS_PASSKEYSの定義は以下です。
create or replace package as_passkeys
is
function get_version
return varchar2;
function render
( p_dynamic_action apex_plugin.t_dynamic_action
, p_plugin apex_plugin.t_plugin
)
return apex_plugin.t_dynamic_action_render_result;
function ajax
( p_dynamic_action apex_plugin.t_dynamic_action
, p_plugin apex_plugin.t_plugin
)
return apex_plugin.t_dynamic_action_ajax_result;
function verify_authentication( p_username varchar2 )
return boolean;
function passkey_authentication
( p_username varchar2
, p_password varchar2
)
return boolean;
end as_passkeys;
パスキーによる認証は、主に動的アクションのプラグインとして作成されています。
ファンクションrenderでは、このカスタム・プラグインを組み込んだページに挿入するHTMLやJavaScriptを生成します。
ファンクションajaxは、ボタンRegisterおよびSign Inをクリックしたときに呼び出される、データベース・サーバー側の処理になります。
ファンクションpasskey_authenticationは、カスタム認証スキームの認証ファンクションとして使用します。実際はユーザー名のみを引数として、ファンクションverify_authenticationを呼び出しています。
ほとんどの実装は動的アクション・プラグインのAS Passkeyに含まれています。
動的アクション・プラグインのAS Passkeyのコールバックのレンダリング・プロシージャ/ファンクション名としてinit_plugin_and_renderが設定されています。
このコードは、プラグインのソースのPL/SQLコードに記述されています。
ファンクションinit_plugin_and_renderでは、if init_table and init_package( p_plugin )という条件で、表AS_USER_PASSKEYSとパッケージAS_PASSKEYSの存在を確認し、それらが存在すればas_passkeys.renderを呼び出して、動的アクション(JavaScript)から呼び出すコードを生成しています。
function init_plugin_and_render
( p_dynamic_action apex_plugin.t_dynamic_action
, p_plugin apex_plugin.t_plugin
)
return apex_plugin.t_dynamic_action_render_result
is
l_rv apex_plugin.t_dynamic_action_render_result;
begin
if init_table and init_package( p_plugin )
then
execute immediate 'begin :x := as_passkeys.render( :p1, :p2 ); end;' using out l_rv, p_dynamic_action, p_plugin;
end if;
return l_rv;
end init_plugin_and_render;
ファンクションinit_tableでは、動的SQLとして以下を実行して、例外が発生したらDDLを実行しています。
declare x as_user_passkeys%rowtype; begin null; end;
ビューUSER_TABLESやALL_TABLESを検索して確認するには、アクセス権限が必要だったり、オブジェクト数が多い場合は検索に時間がかかるので、このような手法は合理的です。
function init_table
return boolean
is
e_not_declared exception;
pragma exception_init( e_not_declared, -6550 );
begin
begin
execute immediate 'declare x as_user_passkeys%rowtype; begin null; end;';
exception
when e_not_declared then
apex_debug.warn( 'table as_user_passkeys does not exist' );
execute immediate '
create table as_user_passkeys
( id number generated always as identity constraint as_user_passkeys2_pk primary key
, workspace_id number not null
, app_id number not null
, name varchar2(4000 char) not null
, extra varchar2(4000 char)
, passkeys clob
)
';
apex_debug.trace( 'table as_user_passkeys created' );
execute immediate '
alter table as_user_passkeys
add constraint as_user_passkeys_uk unique( workspace_id, app_id, name )
';
apex_debug.trace( 'unique key for as_user_passkeys created' );
end;
execute immediate 'declare x as_user_passkeys%rowtype; begin null; end;';
apex_debug.info( 'table as_user_passkeys exists' );
return true;
end init_table;
パッケージAS_PASSKEYSも同じように存在確認に例外を使っています。パッケージ定義部と本体を記述したファイルは、プラグインの添付ファイルになっています。
ファンクションinit_packageではパッケージをインストールするにあたって、プラグインの添付ファイルを実行しています。そのため、静的アプリケーション・ファイルやインストール・スクリプトを別途用意する必要が無く、プラグインだけで、プラグインの実行に必要なデータベース・オブジェクトが作成されます。
ボタンRegisterまたはSign Inをクリックしたときに呼び出されるファンクションとして、パッケージAS_PASSKEYSに含まれるファンクションAJAXが呼び出されるように、コールバックのAJAXプロシージャ/ファンクション名が設定されています。
ボタンRegisterとSign Inの動作は、カスタム属性のUsageによって切り替えています。
Usageでは、RegisterとAuthenticate(値はregisterとauthenticate)を選択できます。
ボタンRegisterをクリックしたときの、動的アクションAS Passkey[プラグイン]の設定のUsageはRegisterに設定されています。つまり、パスキーの登録作業が呼び出されます。
パスキーでサインインするボタンSign In(外観がホットのボタン)をクリックしたときの、動的アクションAS Passkey[プラグイン]の設定のUsageはAuthenticateに設定されています。つまり、パスキーによる認証作業が呼び出されます。
動的アクション・プラグインが組み込まれたページには、AS_PASSKEYS.RENDERが生成したHTML/JavaScriptが挿入されます。
apex_javascript.add_libraryにより、プラグインに含まれているファイルwebauthn.js(またはwebauthn.min.js)がページに組み込まれます。また、ボタンクリック時にファイルwebauthn.jsに記述されているファンクション_webauthnが呼び出されるように記述されています。動的アクションのattribute_01としてプラグインの設定のUsageに設定したregisterまたはauthenticateが渡され、それを引数としてサーバー側のファンクションAS_PASSKEYS.AJAXが呼び出されます。
function render
( p_dynamic_action apex_plugin.t_dynamic_action
, p_plugin apex_plugin.t_plugin
)
return apex_plugin.t_dynamic_action_render_result
is
l_result apex_plugin.t_dynamic_action_render_result;
begin
if apex_application.g_debug
then
apex_plugin_util.debug_dynamic_action
( p_plugin => p_plugin
, p_dynamic_action => p_dynamic_action
);
end if;
apex_debug.trace( '%s render: %s', p_plugin.name, p_dynamic_action.attribute_01 );
apex_javascript.add_library( p_name => 'webauthn#MIN#'
, p_directory => p_plugin.file_prefix
, p_version => null
);
l_result.attribute_01 := p_dynamic_action.attribute_01;
l_result.attribute_02 := p_dynamic_action.attribute_02;
l_result.attribute_03 := p_dynamic_action.attribute_03;
l_result.attribute_04 := p_dynamic_action.attribute_04;
l_result.ajax_identifier := apex_plugin.get_ajax_identifier;
l_result.javascript_function := '_webauthn';
return l_result;
end render;
パスキーの登録と認証のフローは以上です。
最後にAPEXアプリケーションをパスキーで認証した上でセッションを継続するために、認証スキームとしてPasskeysが作成されています。
スキーム・タイプはカスタム、設定の認証ファンクション名としてAS_PASSKEYS.PASSKEY_AUTHENTICATIONが設定されています。
ファンクションAS_PASSKEYS.PASSKEY_AUTHENTICATION(実際はVERIFY_AUTHENTICATION)では、設定のUsageがAuthenticateの動的アクションで認証されたときに、AS_PASSKEYS.AJAXがAPEXコレクションに挿入した認証子の有無を確認して、認証を引き継いでいます。
パスキー自体の生成や確認については、概ねパスキーの仕様に基づいたコードがパッケージAS_PASSKEYSに含まれています。
今回の記事は以上になります。
Oracle APEXのアプリケーション作成の参考になれば幸いです。
完



























