メインコンテンツまでスキップ
プレビュー版
PocketSign Link v2 は現在プレビュー版です。正式提供までに仕様が変更される可能性があります。

In-App SDK から KLON への移行

このガイドは、In-App SDK を利用していたサービスを KLON(KLON IdP / KLON Registry)ベースのフローに移行するための手順を説明します。

移行後はポケットサインアプリ上でもブラウザ上でも同じ仕組みで動作するようになります。

移行の全体像

主な変更点

対応関係

従来(In-App SDK)KLON
In-App SDK 経由の認証・権限要求OIDC 認可フロー(IdP)
In-App SDK 経由のリソース読み書きRegistry API(UserService / ClientService)
独自 JWT(署名付き)標準 OIDC ID Token / Access Token
getSubscriptionIdWithSignature の JWTID Token の sub クレーム

大きな違い

1. 認証・リソースアクセスがバックエンド主導の OIDC フローに変わった

In-App SDK ではフロントエンドが SDK メソッド(getSubscriptionIdWithSignature -> requestPermission -> readResourceWithSignature)を逐次呼び出していました。KLON ではバックエンドで標準的な OIDC 認可フローを実行し、フロントエンドはリダイレクトのみになります。

  • IdP: 認証認可、トークン発行、同意画面
  • Registry: ユーザーリソースの読み書き(ConnectRPC API)

2. SDK のリソース操作が Registry API に置き換わった

観点In-App SDKKLON
ユーザーリソースの読み取りreadResourceWithSignature / readResourceRawRegistryUserService.ReadResourceValues API
ユーザーリソースの書き込みwriteResourceRegistryUserService.WriteResourceValue API
サービスからのリソース操作(SDK 経由のみ)RegistryClientService API
リソース指定リソース ID(UUID)id_or_alias(UUID / エイリアス両対応)
API 形態SDK メソッド呼び出しConnectRPC

詳細はリソースモデルを参照してください。

3. リソースモデルが拡張された

KLON Registry は従来のリソース概念を引き継ぎつつ、次を拡張しています。

  • エイリアス
  • string / json / binary の値フォーマット
  • 算出リソース
  • 検証プロセス情報

特に jsonbinary を明示的に分けた点は、移行時に設計見直しが必要になりやすいです。

4. 権限要求が認可リクエストに統合された

In-App SDK では requestPermission で個別に権限を要求していましたが、KLON では認可リクエスト時に authorization_detailsgrant_management_action で一括指定します。

  • 初回要求: authorization_details
  • 追加権限: grant_management_action=merge
  • 既存置換: grant_management_action=replace

SDK 関数の移行先マッピング

In-App SDK の各関数が KLON でどう変わるかの一覧です。

KLON API で代替する関数

In-App SDK 関数KLON での代替
createSDKInstanceOIDC フロー + Registry API に移行。ポケットサインアプリ限定機能を使う場合のみ In-App SDK 側で使用
requestSubscriptionscopeopenid を含む認可リクエスト
getSubscriptionIdWithSignatureID Token の sub クレーム(= subscription_id
requestPermissionauthorization_details で認可リクエスト時に指定
readResourceRaw / readResourceWithSignatureRegistryUserService API ReadResourceValues / RegistryClientService API ReadResourceValues
readResourcesRaw / readResourcesWithSignature同上(複数リソース版)
writeResourceRegistry.UserService.WriteResourceValue / ClientService.WriteResourceValue
checkCertificateRevocationIdPUserService API CheckCertificateRevocation / IdPClientService API CheckCertificateRevocation
getCurrentGeolocationWeb 標準 navigator.geolocation.getCurrentPosition
shareWeb 標準 navigator.share
downloadFileWeb 標準 Blob + <a download>
openImagePicker / openCameraWeb 標準 <input type="file">

ポケットサインアプリ限定の関数

ブラウザでは代替手段がなく、ポケットサインアプリ内でのみ動作する関数です。In-App SDK 経由で引き続き利用できます。

In-App SDK 関数用途
closeWindowウィンドウを閉じる
openAppSettingsアプリ設定画面を開く
requestLocalAuthentication / getLocalAuthenticationAvailability / openLocalAuthenticationSettings端末生体認証
requestAppUpdateアプリのアップデートを要求
openService別のミニアプリを開く
openServiceSettings / openResourceEditorアプリ内の設定画面を開く
readWithQrScannerネイティブ QR スキャナー

前提

  • KLON のサービス登録が完了していること
  • KLON の OIDC クライアント ID / クライアントシークレットが発行されていること
  • リダイレクト URI が設定されていること
  • KLON SDK がインストールされていること

1. SDK の初期化

createSDKInstance にバックエンドの種別を指定して初期化していました:

import {
createSDKInstance,
createAppBackend,
createApiBackend,
} from "@pocketsign/in-app-sdk";

const instance = await createSDKInstance({
serviceId,
backend: createAppBackend(),
});

KLON ベースでは、認証・リソースアクセスに In-App SDK は使用しません(OIDC フロー + Registry API に移行)。

2. 認証・認可

従来は In-App SDK を通じて、サブスクリプション -> 権限要求 -> リソース読み取りを逐次実行していました:

import {
getSubscriptionIdWithSignature,
MergedSourceResources,
readResourceWithSignature,
requestPermission,
} from "@pocketsign/in-app-sdk";

// 1. サブスクリプション ID を取得(署名付き JWT)
const subscriptionIdRes = await getSubscriptionIdWithSignature(sdk);

// 2. 権限を要求
const resources = [
MergedSourceResources.fullName,
MergedSourceResources.fullAddress,
];
const requestPermissionRes = await requestPermission(
sdk,
resources.map((resourceId) => ({
resourceId,
verbs: [{ verb: "read", term: "always" }],
})),
);

// 3. リソースを 1 つずつ署名付きで読み取り
const fullNameRes = await readResourceWithSignature(sdk, {
resourceId: MergedSourceResources.fullName,
});
const fullAddressRes = await readResourceWithSignature(sdk, {
resourceId: MergedSourceResources.fullAddress,
});

受け取った JWT をポケットサインサーバーの公開鍵(JWKS)で署名検証し、クレームを抽出していました:

import { createRemoteJWKSet, jwtVerify } from "jose";

// ポケットサインサーバーの公開鍵(JWKS)
const JWKS = createRemoteJWKSet(
new URL("https://id.pocketsign.co.jp/.well-known/jwks.json"),
);

// 署名検証 + aud が serviceId と一致するかの検証を同時に行う
const { payload } = await jwtVerify(jwtString, JWKS, {
audience: serviceId,
});

const value = payload[key] as string;

KLON では 1 回の OIDC 認可リクエストでサブスクリプション + 権限要求が完結します。

2.1 OIDC クライアント(バックエンド)

import (
klon "github.com/pocketsign/klon-sdk-go"
)

oidcClient := klon.NewClient(klon.ClientConfig{
Issuer: "https://id.example.com",
ClientID: "your-client-id",
ClientSecret: "your-client-secret", // Confidential Client の場合
RedirectURI: "https://your-service.example.com/callback",
})

2.2 認可リクエスト(バックエンド)

// 認可リクエストを構築(サブスク + 権限要求が 1 回で完結)
boolTrue := true
maxAge := 3600

authURL, session, err := oidcClient.CreateAuthorizationURL(ctx, klon.AuthorizeOptions{
Scopes: []string{klon.ScopeOpenID, klon.ScopeOfflineAccess},
AuthorizationDetails: []klon.AuthorizationDetailInput{
{
Identifiers: []string{klon.ResourceMergedFullName},
Actions: []klon.ResourceAction{klon.ResourceActionRead},
Required: &boolTrue, // 必須リソース
},
{
Identifiers: []string{klon.ResourceMergedBirthDate},
Actions: []klon.ResourceAction{klon.ResourceActionRead},
},
{
Identifiers: []string{klon.ResourceEmailAddress},
Actions: []klon.ResourceAction{klon.ResourceActionRead},
},
},
AcrValues: []string{klon.AcrHigh},
Prompt: []string{klon.PromptConsent},
MaxAge: &maxAge,
GrantManagementAction: klon.GrantManagementReplace,
UsePAR: true, // Pushed Authorization Request (RFC 9126)
})

// session をセッションストレージに保存(コールバック時の検証に使用)
// JSON シリアライズ可能: json.Marshal(session) / json.Unmarshal(..., &session)
sessionStore.Save(session)
http.Redirect(w, r, authURL.String(), http.StatusFound)

UsePAR: true を推奨。認可パラメータを事前に IdP に送信し、URL にパラメータを含めない。

authorization_details の詳細は authorization_details と同意モデルを参照してください。

2.3 コールバック処理(バックエンド)

// /callback ハンドラー
state := r.URL.Query().Get("state")
session, ok := sessionStore.Load(state)
if !ok {
http.Error(w, "invalid state", http.StatusBadRequest)
return
}

code := r.URL.Query().Get("code")
tokenSet, err := oidcClient.ExchangeCode(ctx, code, state, session)

// tokenSet.AccessToken — ユーザーアクセストークン(Registry API 呼び出し用)
// tokenSet.IDToken — ID Token(sub = Subscription ID)
// tokenSet.RefreshToken — リフレッシュトークン(offline_access スコープ時)
// tokenSet.AuthorizationDetails — 付与された権限の詳細

ExchangeCodestate パラメータの一致検証を内部で行う。

2.4 トークンリフレッシュ(バックエンド)

newTokenSet, err := oidcClient.RefreshToken(ctx, tokenSet.RefreshToken)

3. 追加権限の要求

初回ログイン後に、追加でリソースへのアクセス権限を要求するケースです。

従来は requestPermission を任意のタイミングで再度呼び出すことで、追加の権限を要求できました:

import { requestPermission, MergedSourceResources } from "@pocketsign/in-app-sdk";

// 後から追加で住所の読み取り権限を要求
const result = await requestPermission(sdk, [
{
resourceId: MergedSourceResources.fullAddress,
verbs: [{ verb: "read", term: "always" }],
},
]);

KLON では、grant_management_action=merge を指定した認可リクエストを再度発行します。既存のグラントに新しい権限だけが追加され、差分がある場合のみ同意画面が表示されます。

authURL, session, err := oidcClient.CreateAuthorizationURL(ctx, klon.AuthorizeOptions{
Scopes: []string{klon.ScopeOpenID, klon.ScopeOfflineAccess},
AuthorizationDetails: []klon.AuthorizationDetailInput{
{
// 追加で要求するリソースだけを指定
Identifiers: []string{klon.ResourceMergedFullAddress},
Actions: []klon.ResourceAction{klon.ResourceActionRead},
},
},
GrantManagementAction: klon.GrantManagementMerge, // 既存グラントに追加
UsePAR: true,
})

コールバック処理は初回と同じです。トークン交換後に返される tokenSet には、追加権限を含む新しいアクセストークンとリフレッシュトークンが含まれます。

詳細は後から追加権限を要求するを参照してください。

4. リソース読み取り

従来は SDK でリソースを署名付き JWT として読み取り、バックエンドで署名検証していました:

import { createRemoteJWKSet, jwtVerify } from "jose";
// SDK で署名付きリソースを取得
const fullNameRes = await readResourceWithSignature(sdk, {
resourceId: MergedSourceResources.fullName,
});
// fullNameRes.value は JWT 文字列
// JWKS で JWT 署名を検証し、クレームを抽出
const JWKS = createRemoteJWKSet(
new URL("https://id.pocketsign.co.jp/.well-known/jwks.json"),
);

// 署名検証 + aud が serviceId と一致するかの検証を同時に行う
const { payload } = await jwtVerify(jwtString, JWKS, {
audience: serviceId,
});

const value = payload.value as string;

OIDC で取得したアクセストークンで Registry UserService を呼び出します。

// ConnectRPC クライアントを作成(ユーザーアクセストークンを付与)
userServiceClient := linkv2connect.NewRegistryUserServiceClient(
&http.Client{Transport: &bearerTransport{token: tokenSet.AccessToken}},
registryURL,
)

// 複数リソースを一括取得(最大 50 個)
resp, err := userServiceClient.ReadResourceValues(ctx,
connect.NewRequest(&linkv2.RegistryUserServiceReadResourceValuesRequest{
IdOrAliases: []string{
klon.ResourceMergedFullName, // "klon/merged_full_name"
klon.ResourceMergedBirthDate, // "klon/merged_birth_date"
klon.ResourceEmailAddress, // "klon/email_address"
},
}),
)

// 各リソースの結果を処理
for _, rv := range resp.Msg.Results {
if rv.Error != linkv2.ReadResourceValueError_READ_RESOURCE_VALUE_ERROR_UNSPECIFIED {
// READ_RESOURCE_VALUE_ERROR_NO_VALUE
// READ_RESOURCE_VALUE_ERROR_UNKNOWN_RESOURCE
// READ_RESOURCE_VALUE_ERROR_NO_PERMISSION
// READ_RESOURCE_VALUE_ERROR_SUBSCRIPTION_NOT_FOUND
// READ_RESOURCE_VALUE_ERROR_PERMISSION_SUSPENDED
// READ_RESOURCE_VALUE_ERROR_PERMISSION_EXPIRED
continue
}
// rv.Value.GetJson(), rv.Value.GetString_(), rv.Value.GetBinary()
}

もしくは、サービスアクセストークンで Registry ClientService を呼び出します。 subscription_id を指定して特定ユーザーのリソースを取得します。

// ConnectRPC クライアントを作成(サービスアクセストークンを付与)
clientServiceClient := linkv2connect.NewRegistryClientServiceClient(
&http.Client{Transport: &bearerTransport{token: serviceAccessToken}},
registryURL,
)

// 複数ユーザー x 複数リソースを一括取得(最大 50 個)
resp, err := clientServiceClient.ReadResourceValues(ctx,
connect.NewRequest(&linkv2.RegistryClientServiceReadResourceValuesRequest{
UserResources: []*linkv2.RegistryClientServiceReadResourceValuesRequest_UserResource{
{IdOrAlias: klon.ResourceMergedFullName, SubscriptionId: subscriptionID},
{IdOrAlias: klon.ResourceMergedBirthDate, SubscriptionId: subscriptionID},
},
}),
)

5. リソース書き込み

import { writeResource } from "@pocketsign/in-app-sdk";

await writeResource(sdk, {
resourceId: FAVORITE_FOOD_RESOURCE_ID,
resource: { value: JSON.stringify("寿司") },
});
// Registry UserService.WriteResourceValue
resp, err := userServiceClient.WriteResourceValue(ctx,
connect.NewRequest(&linkv2.RegistryUserServiceWriteResourceValueRequest{
IdOrAlias: "your-custom-resource-alias",
// 3 つのフォーマットから 1 つを選択:
Value: &linkv2.RegistryUserServiceWriteResourceValueRequest_Json{
Json: structpb.NewStringValue("寿司"),
},
// Value: &linkv2.RegistryUserServiceWriteResourceValueRequest_String_{String_: "寿司"},
// Value: &linkv2.RegistryUserServiceWriteResourceValueRequest_Binary{Binary: &linkv2.ResourceValueBinary{Bytes: bytes, MimeType: "image/png"}},
}),
)

サービスアクセストークンを使って ClientService で書き込む場合は、subscription_id を指定する必要があります。

// Registry ClientService.WriteResourceValue
resp, err := clientServiceClient.WriteResourceValue(ctx,
connect.NewRequest(&linkv2.RegistryClientServiceWriteResourceValueRequest{
IdOrAlias: "your-custom-resource-alias",
SubscriptionId: subscriptionID,
Value: &linkv2.RegistryClientServiceWriteResourceValueRequest_Json{
Json: structpb.NewStringValue("寿司"),
},
}),
)

6. 電子証明書の失効確認

import { checkCertificateRevocation } from "@pocketsign/in-app-sdk";

const result = await checkCertificateRevocation(sdk, {
type: "jpki_card_digital_signature",
});
// IdP UserService.CheckCertificateRevocation
resp, err := idpUserServiceClient.CheckCertificateRevocation(ctx,
connect.NewRequest(&linkv2.IdPUserServiceCheckCertificateRevocationRequest{
CertificateType: linkv2.CertificateType_CERTIFICATE_TYPE_JPKI_CARD_DIGITAL_SIGNATURE,
}),
)
// resp.Msg.Status: CERTIFICATE_STATUS_GOOD | CERTIFICATE_STATUS_REVOKED
// resp.Msg.RevocationReason: CERTIFICATE_REVOCATION_REASON_KEY_COMPROMISE | CERTIFICATE_REVOCATION_REASON_CA_COMPROMISE | ...
// resp.Msg.RevokedAt: *timestamppb.Timestamp

サービスアクセストークンで ClientService を呼び出す場合は、subscription_id を指定する必要があります。

// IdP ClientService.CheckCertificateRevocation
resp, err := idpClientServiceClient.CheckCertificateRevocation(ctx,
connect.NewRequest(&linkv2.IdPClientServiceCheckCertificateRevocationRequest{
SubscriptionId: subscriptionID,
CertificateType: linkv2.CertificateType_CERTIFICATE_TYPE_JPKI_CARD_DIGITAL_SIGNATURE,
}),
)

証明書タイプ一覧:

従来の SDK 値KLON CertificateType
jpki_card_digital_signatureCERTIFICATE_TYPE_JPKI_CARD_DIGITAL_SIGNATURE
jpki_card_user_authenticationCERTIFICATE_TYPE_JPKI_CARD_USER_AUTHENTICATION
jpki_mobile_digital_signatureCERTIFICATE_TYPE_JPKI_MOBILE_DIGITAL_SIGNATURE
(なし)CERTIFICATE_TYPE_JPKI_MOBILE_USER_AUTHENTICATION

7. 位置情報

import { getCurrentGeolocation } from "@pocketsign/in-app-sdk";

const result = await getCurrentGeolocation(sdk, {
enableHighAccuracy: true,
maximumAge: 0,
});
// Web 標準 API を直接使用する
const position = await new Promise<GeolocationPosition>((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve, reject, {
enableHighAccuracy: true,
maximumAge: 0,
});
});

Link v2 のサービスが未対応のバージョンのポケットサインアプリで開かれた場合にアップデートを要求します。requestAppUpdate は SDK インスタンスなしで呼び出せます。

import {
isKLONSupported,
requestAppUpdate,
} from "@pocketsign/in-app-sdk";

if (!isKLONSupported()) {
requestAppUpdate();
return;
}

// KLON ベースのフローへ

9. ポケットサインアプリ / ブラウザの判別

ポケットサインアプリ固有の機能(closeWindow 等)を使う場合に環境判別が必要です。

import { isInApp } from "@pocketsign/in-app-sdk";

if (isInApp()) {
// ポケットサインアプリ内で動作中
// closeWindow 等のネイティブ機能が使える
} else {
// ブラウザで動作中
// ネイティブ機能は使えない
}

リファレンス

ACR Values

認証の強度を制御するために acrValues を指定します。

ACR Value定数(TS)定数(Go)説明
urn:klon:acr:lowAcrValues.LOWklon.AcrLowメール / 外部 IdP
urn:klon:acr:highAcrValues.HIGHklon.AcrHighJPKI 利用者証明用電子証明書
urn:klon:acr:very_highAcrValues.VERY_HIGHklon.AcrVeryHighJPKI 署名用電子証明書

Scopes

Scope定数(TS)定数(Go)説明
openidScopes.OPENIDklon.ScopeOpenIDOpenID Connect(必須)
profileScopes.PROFILEklon.ScopeProfile氏名 / 生年月日 / 性別
emailScopes.EMAILklon.ScopeEmailメールアドレス
phoneScopes.PHONEklon.ScopePhone電話番号
addressScopes.ADDRESSklon.ScopeAddress住所
personal_infoScopes.PERSONAL_INFOklon.ScopePersonalInfo基本4情報
offline_accessScopes.OFFLINE_ACCESSklon.ScopeOfflineAccessリフレッシュトークン

Scopes の詳細はリソース権限要求を参照してください。

リソースエイリアスの対応表

利用可能なリソース定義の完全な一覧はリソース定義一覧を参照してください。

注記

In-App SDK の MergedSourceResources.* 定数が参照する UUID は、KLON Registry では klon/legacy_merged_* エイリアスのリソースに対応しています。 移行先としては、署名用電子証明書と券面事項入力補助APの更新日時を比較して新しい方を返すリソース klon/merged_* を推奨します。どちらにも値がない場合は手入力の値にフォールバックします。

マージ済み(最新値を採用)

従来のリソースKLON エイリアスTS 定数Go 定数
MergedSourceResources.firstNameklon/merged_first_nameResources.MERGED_FIRST_NAMEklon.ResourceMergedFirstName
MergedSourceResources.lastNameklon/merged_last_nameResources.MERGED_LAST_NAMEklon.ResourceMergedLastName
(なし)klon/merged_maiden_nameResources.MERGED_MAIDEN_NAMEklon.ResourceMergedMaidenName
MergedSourceResources.fullNameklon/merged_full_nameResources.MERGED_FULL_NAMEklon.ResourceMergedFullName
(なし)klon/merged_popular_nameResources.MERGED_POPULAR_NAMEklon.ResourceMergedPopularName
MergedSourceResources.birthDateklon/merged_birth_dateResources.MERGED_BIRTH_DATEklon.ResourceMergedBirthDate
MergedSourceResources.genderklon/merged_genderResources.MERGED_GENDERklon.ResourceMergedGender
MergedSourceResources.fullAddressklon/merged_full_addressResources.MERGED_FULL_ADDRESSklon.ResourceMergedFullAddress
MergedSourceResources.prefectureAddressklon/merged_prefecture_addressResources.MERGED_PREFECTURE_ADDRESSklon.ResourceMergedPrefectureAddress
MergedSourceResources.cityAddressklon/merged_city_addressResources.MERGED_CITY_ADDRESSklon.ResourceMergedCityAddress
(なし)klon/merged_moving_abroad_dateResources.MERGED_MOVING_ABROAD_DATEklon.ResourceMergedMovingAbroad
MergedSourceResources.birthYearklon/merged_birth_yearResources.MERGED_BIRTH_YEARklon.ResourceMergedBirthYear
MergedSourceResources.birthDayklon/merged_birth_dayResources.MERGED_BIRTH_DAYklon.ResourceMergedBirthDay

その他

従来のリソースKLON エイリアスTS 定数Go 定数
EmailResourceIdklon/email_addressResources.EMAIL_ADDRESSklon.ResourceEmailAddress
PhoneNumberResourceIdklon/phone_numberResources.PHONE_NUMBERklon.ResourcePhoneNumber
FaceImageResourceIdklon/face_imageResources.FACE_IMAGEklon.ResourceFaceImage

フリガナ(klon/merged_first_name_ruby 等)、通称(klon/merged_popular_name 等)、旧姓(klon/merged_maiden_name 等)、海外転出(klon/merged_moving_abroad_date 等)、署名用(klon/signing_*)、券面事項入力補助(klon/ticket_*)、手入力(klon/manual_*)も利用可能です。

完全な一覧は KLON SDK の Resources(TypeScript)/ Resource* 定数(Go)を参照してください。


プッシュ通知

プッシュ通知(PushNotificationResourceId / Service API CreateNotification)は、引き続き従来の仕組みを利用してください。

イベントウェブフック

イベントウェブフックは KLON でも利用できます。詳細は Webhook イベントを参照してください。


関連ページ