Subscriber / Service API から KLON への移行
このページでは、Subscriber API / Service API を使っていた開発者が、KLON(KLON IdP / KLON Registry)ベースのフローに移行するときに押さえるべき差分を整理します。
特に Subscriber API、Service API、OIDC、リソース定義、識別子の扱いが変わる点を中心にまとめます。
:::tip In-App SDK から KLON への移行 In-App SDK を使っていた場合は In-App SDK から KLON への移行 を参照してください。 このページは Subscriber API(外部ブラウザで開くサービス等)からの移行を主に扱います。 :::
まず押さえる対応関係
| Subscriber / Service API | KLON |
|---|---|
OIDC 認可サーバー(https://oidc.<host>) | IdP(https://id.<host>) |
Subscriber API | Registry.UserService |
Service API | Registry.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 API が UserService / ClientService に置き換わった
| 観点 | Subscriber / Service API | KLON |
|---|---|---|
| ユーザー本人のリソース操作 | Subscriber API | Registry.UserService |
| サービスとしてのユーザーリソース操作 | Service API | Registry.ClientService |
| API 形態 | ConnectRPC | ConnectRPC |
| リソース指定 | 既存リソース定義中心 | id_or_alias で ID / エイリアス両対応 |
3. リソースモデルが拡張された
v2 の Registry は Link の基本概念を引き継ぎつつ、次を拡張しています。
- エイリアス
string/json/binaryの値フォーマット- 算出リソース
- 検証プロセス情報
特に json と binary を明示的に分けた点は、移行時に設計見直しが必要になりやすいです。
4. 権限要求が authorization_details 中心になった
Link でも OIDC による権限付与はありましたが、v2 では Registry リソース要求を authorization_details と grant_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を使う
Link v1
// 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)
Link v2
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 の生成・検証を自動で行う
コールバック・トークン交換
Link v1
// 標準的な 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 がユーザー識別子
Link v2
// 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_valuesjwks_uridpop_bound_access_tokens
トークン利用
userinfoだけでなく Registry API を呼ぶ前提でトークンを設計する- DPoP を使うなら
userinfoだけでなく RegistryUserServiceでも 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で指定する
Link v1
// 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,
})
Link v2
// 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("寿司"),
},
}))
主な変更点:
SubscriberService→Registry.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
Link v1
// 例: 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() で値を取得
Link v2
// 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_details、required、prefill をどう使うかまで設計対象になります。
識別子の移行ポイント
| 用途 | Link v1 での考え方 | Link v2 での考え方 |
|---|---|---|
| OIDC の利用者識別 | sub | sub(常に subscription_id) |
| サービスごとの加入者識別 | sub に近い感覚で扱うことが多い | subscription_id を明示して扱う |
| Registry / Service 側の対象指定 | API ごとの仕様に依存 | ClientService は subscription_id 必須 |
移行時は「Link で sub に入っていた概念が、v2 でも sub(= subscription_id)としてそのまま扱える」と考えると分かりやすいです。
先に読むと良いページ
- OIDC の全体像は OIDC の概要
- 権限要求は authorization_details と同意モデル
- Registry 側の差分は リソースモデル
- In-App SDK からの移行は In-App SDK から KLON への移行