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

Subscriber / Service API から KLON への移行

このページでは、Subscriber API / Service API を使っていた開発者が、KLON(KLON IdP / KLON Registry)ベースのフローに移行するときに押さえるべき差分を整理します。 特に Subscriber APIService API、OIDC、リソース定義、識別子の扱いが変わる点を中心にまとめます。

:::tip In-App SDK から KLON への移行 In-App SDK を使っていた場合は In-App SDK から KLON への移行 を参照してください。 このページは Subscriber API(外部ブラウザで開くサービス等)からの移行を主に扱います。 :::

まず押さえる対応関係

Subscriber / Service APIKLON
OIDC 認可サーバー(https://oidc.<host>IdPhttps://id.<host>
Subscriber APIRegistry.UserService
Service APIRegistry.ClientService
Subscriber API / Service API のユーザーリソースRegistry のリソース値
サービスごとの加入者識別子subscription_id
リソース ID(UUID)id_or_alias(UUID またはエイリアス)
display=psapp(ログインショートカット)廃止(後述)

大きな違い

1. OIDC と Resource Server が整理された

Link では OIDC と Subscriber API / Service API を合わせて理解する必要がありました。 v2 では役割が明確に分かれます。

  • IdP: 認証認可、トークン発行、再認証、同意
  • Registry: ユーザーリソースの読み書き

そのため、OIDC で取得したトークンを使って、次に Registry API を呼ぶ、という流れがより明確です。

2. Subscriber API / Service APIUserService / ClientService に置き換わった

観点Subscriber / Service APIKLON
ユーザー本人のリソース操作Subscriber APIRegistry.UserService
サービスとしてのユーザーリソース操作Service APIRegistry.ClientService
API 形態ConnectRPCConnectRPC
リソース指定既存リソース定義中心id_or_alias で ID / エイリアス両対応

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

v2 の Registry は Link の基本概念を引き継ぎつつ、次を拡張しています。

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

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

4. 権限要求が authorization_details 中心になった

Link でも OIDC による権限付与はありましたが、v2 では Registry リソース要求を authorization_detailsgrant_management_action で扱うのが中心です。

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

OIDC の移行ポイント

認可リクエスト

Link からの移行でも、基本は Authorization Code Flow のまま考えられます。 ただし v2 では、次を強く意識すると実装しやすくなります。

  • PKCE は S256 前提
  • 複雑な権限要求は PAR を使う
  • リソース権限は authorization_details で表す
  • 追加権限は grant_management_action=merge を使う
// go-oidc + oauth2 で標準的な Authorization Code Flow
provider, _ := oidc.NewProvider(ctx, "https://oidc."+host)

oauth2Config := oauth2.Config{
ClientID: os.Getenv("OIDC_CLIENT_ID"),
ClientSecret: os.Getenv("OIDC_CLIENT_SECRET"),
RedirectURL: os.Getenv("OIDC_REDIRECT_URL"),
Endpoint: provider.Endpoint(),
Scopes: []string{oidc.ScopeOpenID, oidc.ScopeOfflineAccess},
}

// 認可 URL へリダイレクト
http.Redirect(w, r, oauth2Config.AuthCodeURL(state), http.StatusFound)
import klon "github.com/pocketsign/klon-sdk-go"

oidcClient := klon.NewClient(klon.ClientConfig{
Issuer: "https://id.<host>",
ClientID: "your-client-id",
ClientSecret: "your-client-secret",
RedirectURI: "https://your-service/callback",
})

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},
},
},
UsePAR: true, // PAR 推奨
})
// session をセッションストアに保存
http.Redirect(w, r, authURL.String(), http.StatusFound)

主な変更点:

  • リソース権限を authorization_details で認可リクエスト時に指定
  • PAR(Pushed Authorization Requests)の利用を推奨
  • SDK が PKCE の生成・検証を自動で行う

コールバック・トークン交換

// 標準的な oauth2 トークン交換
oauth2Token, err := oauth2Config.Exchange(r.Context(), r.URL.Query().Get("code"))

// ID Token の検証
rawIDToken, _ := oauth2Token.Extra("id_token").(string)
verifier := provider.Verifier(&oidc.Config{ClientID: oauth2Config.ClientID})
idtoken, err := verifier.Verify(r.Context(), rawIDToken)
// idtoken.Subject がユーザー識別子
// SDK が state 検証・PKCE 検証を内部で実行
state := r.URL.Query().Get("state")
code := r.URL.Query().Get("code")
tokenSet, err := oidcClient.ExchangeCode(ctx, code, state, session)

// tokenSet.IDToken -- sub = subscription_id
// tokenSet.AccessToken -- Registry API 呼び出し用
// tokenSet.RefreshToken
// tokenSet.AuthorizationDetails -- 付与された権限

主な変更点:

  • SDK が state 検証・PKCE 検証を自動で行う
  • AuthorizationDetails で付与された権限の詳細が取得できる
  • ID Token の検証も SDK 内で完結

クライアント設定

Link で最低限 client_id / client_secret / redirect_uri を見ていた実装でも、v2 では次の項目が実装挙動に強く効きます。

  • default_acr_values
  • jwks_uri
  • dpop_bound_access_tokens

トークン利用

  • userinfo だけでなく Registry API を呼ぶ前提でトークンを設計する
  • DPoP を使うなら userinfo だけでなく Registry UserService でも proof を付ける
  • merge 後は新しいアクセストークンへ切り替える

ログインショートカット(display=psapp の廃止)

:::caution display=psapp は廃止されます Link では display=psapp を認可リクエストに付与することで、ポケットサインアプリ内で同意画面をスキップしてログインできました。 v2 ではこのパラメータは廃止されます。移行時に認可リクエストから display=psapp を削除してください。 :::

新しいポケットサインアプリ上の WebView ではセッションバインドの仕組みにより、ログイン済みユーザーは自動的に認証が完了します。 そのため、display=psapp に相当する明示的なパラメータは不要です。 詳細はポケットサインアプリ連携を参照してください。

API 単位の移行

Subscriber API -> Registry.UserService

共通点:

  • ユーザーアクセストークンで本人のリソースを扱う
  • OIDC でログインしてから API を呼ぶ

違い:

  • 呼び出し先は Subscriber API / Service API ではなく https://registry.<env>.klon.you
  • リソースは id_or_alias で指定する
// ConnectRPC で SubscriberService を呼び出し
subscriberClient := linkv1connect.NewSubscriberServiceClient(
http.DefaultClient, "https://api."+host)

// リソース取得(UUID で指定)
req := connect.NewRequest(&linkv1.SubscriberServiceGetUserResourceRequest{
ResourceId: resourceID, // UUID
})
req.Header().Add("Authorization", "Bearer "+accessToken)
resp, err := subscriberClient.GetUserResource(ctx, req)
// resp.Msg.GetValue() で値を取得

// リソース更新
req := connect.NewRequest(&linkv1.SubscriberServiceUpdateUserResourceRequest{
ResourceId: resourceID,
Value: jsonValue,
})
// ConnectRPC で Registry UserService を呼び出し
userClient := linkv2connect.NewRegistryUserServiceClient(
&http.Client{Transport: &bearerTransport{token: tokenSet.AccessToken}},
registryURL)

// リソース取得(エイリアスで指定、最大 50 個一括)
resp, err := userClient.ReadResourceValues(ctx,
connect.NewRequest(&linkv2.RegistryUserServiceReadResourceValuesRequest{
IdOrAliases: []string{
klon.ResourceMergedFullName,
klon.ResourceEmailAddress,
},
}))

// リソース更新
resp, err := userClient.WriteResourceValue(ctx,
connect.NewRequest(&linkv2.RegistryUserServiceWriteResourceValueRequest{
IdOrAlias: "your-custom-resource-alias",
Value: &linkv2.RegistryUserServiceWriteResourceValueRequest_Json{
Json: structpb.NewStringValue("寿司"),
},
}))

主な変更点:

  • SubscriberServiceRegistry.UserService(ユーザートークン)/ Registry.ClientService(サービストークン)
  • リソース指定が UUID のみ → UUID またはエイリアス(id_or_alias
  • 単件取得 → ReadResourceValues で最大 50 個一括取得
  • 値フォーマットが string / json / binary の 3 種に拡張
  • リソース削除、権限確認 API が新規追加

Service API -> Registry.ClientService

共通点:

  • サービスとしてユーザーリソースを読む・書く
  • 対象ユーザーをサービススコープの識別子で扱う

違い:

  • Link はサービスシークレットをそのまま Authorization: Bearer で送る方式だった
  • v2 はサービスアクセストークン前提
  • 対象ユーザー指定は subscription_id
// 例: ConnectRPC で ServiceService を呼び出し
// サービスシークレットをそのまま Authorization ヘッダーで送る
serviceClient := linkv1connect.NewServiceServiceClient(
http.DefaultClient, "https://api."+host)

req := connect.NewRequest(&linkv1.ServiceServiceGetUserResourceRequest{
SubscriberId: subscriberID,
ResourceId: resourceID,
})
req.Header().Add("Authorization", "Bearer "+os.Getenv("SERVICE_SECRET"))
resp, err := serviceClient.GetUserResource(ctx, req)
// resp.Msg.GetValue() で値を取得
// 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},
},
}),
)

リソース定義の移行ポイント

値の型

Link では JSON Schema 中心で考えることが多かった一方、v2 では value_format を先に決めます。

Link v1 での考え方Link v2 での考え方
JSON Schema でユーザーリソース構造を定義string / json / binary を選んだうえで必要なら json_schema を定義

命名

移行時は、UUID だけでなくエイリアスも一緒に設計すると使いやすくなります。

例:

my_org/favorite-food
my_org/account_profile
my_org/account_export

権限設計

Link で Read / Write を中心に見ていたリソースでも、v2 では authorization_detailsrequiredprefill をどう使うかまで設計対象になります。

識別子の移行ポイント

用途Link v1 での考え方Link v2 での考え方
OIDC の利用者識別subsub(常に subscription_id
サービスごとの加入者識別sub に近い感覚で扱うことが多いsubscription_id を明示して扱う
Registry / Service 側の対象指定API ごとの仕様に依存ClientServicesubscription_id 必須

移行時は「Link で sub に入っていた概念が、v2 でも sub(= subscription_id)としてそのまま扱える」と考えると分かりやすいです。

先に読むと良いページ