IBM Cloud Docs
属性の保管とアクセス

属性の保管とアクセス

IBM Cloud® App ID を使用すると、アプリケーションの個々のユーザーに関する情報を収集してプロファイルに入れることができます。 プロファイル内のユーザーに関する情報は、ユーザーとアプリの対話を通して取得するか、管理者がユーザーの代わりに追加することができます。 情報を保管して、ユーザーに合わせてパーソナライズしたアプリ・エクスペリエンスを作成するために利用することができます。

プロファイルについて

ユーザー・プロファイルは、特定のユーザーに関する既知のすべての情報を 1 つの JSON オブジェクトにまとめたものであり、App ID によって保管されます。 取得してプロファイルに保管できる情報や属性には、predefinedcustomという 2 つのタイプがあります。 事前定義属性はユーザーの ID に固有のもので、ユーザーがアプリにサインインするときに ID プロバイダーから返されます。事前定義属性には、名前や年齢などの情報を含めることができます。 カスタム属性は、ユーザーに関する追加情報を保管するために使用します。 管理者が設定することも、ユーザーとアプリの対話の中で収集することもできます。 カスタム属性には、割り当てられた役割、食べ物の好み、飛行機では通路側座席を好むことなどを含めることができます。

caption-side=bottom"
App ID ユーザープロファイル ユーザープロファイル情報の流れ

ユーザーごとに最大 100 KB の情報を保管できます。

ユーザー・プロファイル情報の取得方法

ユーザー情報には、いくつかの異なる方法でアクセスできます。アクセスする必要がある理由もいくつかあります。 呼び出すエンドポイントは、ユース・ケースによって異なります。

API を使用する必要がある場合は、以下のイメージおよび対応する情報を参照して、情報の取得方法を理解してください。

caption-side=bottom"
App ID ユーザープロファイルのエンドポイントオプション ユーザー情報へのアクセスに使用できるエンドポイントオプション

/oauth/v4/<tenantID>/token
認証が成功すると、最も一般的なユーザー情報 (名前、写真、E メールなど) を含むアクセス・トークンと ID トークンを受け取ります。 その他の情報を追加する場合は、カスタム・クレーム・マッピングを使用して、トークンを返す前に追加情報をトークンに注入するように App ID を構成します。
/oauth/v4/<tenantID>/userinfo
ID プロバイダーから返されるユーザー・プロファイル情報の詳細なビューを表示する必要がある場合は、/userinfo エンドポイントを呼び出します。 このエンドポイントは、追加のネットワーク・コールを必要とするため、情報をトークンにマッピングできない場合にのみ使用することを推奨する。
/api/v1/attributes
現在ログインしているユーザーのカスタム・プロファイル属性の読み取りおよび更新をアプリケーションで行う必要がある場合は、/attributes エンドポイントを使用します。 例えば、ユーザーが食べ物の好みを更新することができます。
/management/v4/<tenantID>/users
複数のユーザーに適用される可能性がある管理インターフェースまたはプロセスを作成する場合は、App ID の管理 API を使用します。 特に、/users エンドポイントを使用します。

ユーザー情報を操作する最も簡単な方法は、GUI または SDK を使用する方法です。 これらのオプションを使用すると、すべての API 呼び出しがバックグラウンドで実行されます。

実行時の属性へのアクセス

ユーザー認証が成功すると、アプリは App ID からアクセス・トークンと識別トークンを受け取ります。 アクセス・トークンと識別トークンには、サービスによって属性のサブセットが自動的に挿入されています。 情報がトークン内にない場合は、以下のエンドポイントを使用して情報を取得できます。

/userinfo エンドポイントへのアクセス

構成した ID プロバイダーから提供されたユーザーに関する情報を表示するには、事前定義属性にアクセスします。

  1. openid スコープを持つ有効なアクセス・トークンを所有していることを確認してください。 /introspect エンドポイントを使用して、トークンが有効であることを確認できます。

  2. /userinfo エンドポイントにリクエストを行う。 新しいトークンが SDK に明示的に渡されない場合、App ID は最後に受信したトークンを使用して応答の取得と検証を行います。 識別トークンを渡すことはオプションですが、渡された場合には、応答の検証に使用されます。

    GET https://<region>.appid.cloud.ibm.com/oauth/v4/<tenantID>/userinfo
    Authorization: 'Bearer <accessToken>'
    
    // iOS Swift example
    
    AppID.sharedInstance.userProfileManager.getUserInfo(accessToken: String, identityToken: String?) { (error: Error?, userInfo: [String: Any]?) in guard
       let userInfo = userInfo, err == nil {
          return // an error has occurred
       }
       // retrieved user info successfully
    }
    
    AppID appId = AppID.getInstance();
    
    appId.getUserProfileManager().getUserInfo(accessToken, identityToken, new UserProfileResponseListener() {
       @Override
       public void onSuccess(JSONObject userInfo) {
       // retrieved attribute "name" successfully
       }
    
       @Override
       public void onFailure(UserInfoException e) {
       // exception occurred
       }
    });
    
    let userProfileManager = UserProfileManager(options: options)
    
    let accessToken = req.session[WebAppStrategy.AUTH_CONTEXT].accessToken;
    let identityToken = req.session[WebAppStrategy.AUTH_CONTEXT].identityToken;
    
    // Retrieve user info and validate against the given identity token
    userProfileManager.getUserInfo(accessToken, identityToken).then(function (profile) {
       // retrieved user info successfully
    });
    
    // Retrieve user info without validation
    userProfileManager.getUserInfo(accessToken).then(function (profile) {
    // retrieved user info successfully
    });
    
    // Server-side Swift example
    
    let userProfileManager = UserProfileManager(options: options)
    let accessToken = "<accessToken>"
    let identityToken = "<identityToken>"
    
    // If identity token is provided (recommended approach), response is validated against the identity token
    
    userProfileManager.getUserInfo(accessToken: accessToken, identityToken: identityToken) { (err, userInfo) in guard
       let userInfo = userInfo, err == nil {
       return
       }
    }
    
    // Retrieve the UserInfo without any validation
    
    userProfileManager.getUserInfo(accessToken: accessToken) { (err, userInfo) in guard
       let userInfo = userInfo, err == nil {
       return
       }
    }
    

    出力例:

    "sub": "cad9f1d4-e23b-3683-b81b-d1c4c4fd7d4c",
    "name": "John Doe",
    "email": "john.doe@gmail.com",
    "picture": "https://lh3.googleusercontent.com/-XdUIqdbhg/AAAAAAAAI/AAAAAAA/42rbcbv5M/photo.jpg",
    "gender": "male",
    "locale": "en",
    "identities": [
       {
             "provider": "google",
             "id": "104560903311317789798",
             "profile": {
                "id": "104560903311317789798",
                "email": "john.doe@gmail.com",
                "verified_email": true,
                "name": "John Doe",
                "given_name": "John",
                "family_name": "Doe",
                "link": "https://plus.google.com/104560903311317789798",
                "picture": "https://lh3.googleusercontent.com/-XdUIqdbhg/AAAAAAAAI/AAAAAAA/42rbcbv5M/photo.jpg",
                "gender": "male",
                "locale": "en",
                "idpType": "google"
             }
       }
    ]
    
  3. この sub クレームが識別トークン内の sub クレームと正確に一致することを確認します。 それらが一致しない場合は、返された情報を使用しないでください。 トークン置換の詳細については、 OIDC仕様を参照のこと。

外部の ID プロバイダーによって変更が行われた場合は、ユーザーが再度ログインするときに、更新された情報を取得できます。 新しいトークンにより、最新のデータが取得されます。

/attributes エンドポイントへのアクセス

構成に応じて、ユーザーがアプリケーションと対話するときに、属性が暗号化され、ユーザー・プロファイルの一部として保存されます。 この対話には、ユーザーによるアプリへのサインインやアプリでの設定などが含まれます。 属性にアクセスするには、API メソッドを使用してアクセス・トークンを渡します。

curl -X GET 'https://<region>.appid.cloud.ibm.com/api/v1/attributes'
-H 'Accept: application/json'
-H 'Authorization: Bearer <accessToken>'
//iOS Swift example

func setAttribute(key: String, value: String, completionHandler: @escaping(Error?, [String:Any]?) -> Void)
func setAttribute(key: String, value: String, accessTokenString: String, completionHandler: @escaping(Error?, [String:Any]?) -> Void)

func getAttribute(key: String, completionHandler: @escaping(Error?, [String:Any]?) -> Void)
func getAttribute(key: String, accessTokenString: String, completionHandler: @escaping(Error?, [String:Any]?) -> Void)

func getAttributes(completionHandler: @escaping(Error?, [String:Any]?) -> Void)
func getAttributes(accessTokenString: String, completionHandler: @escaping(Error?, [String:Any]?) -> Void)

func deleteAttribute(key: String, completionHandler: @escaping(Error?, [String:Any]?) -> Void)
func deleteAttribute(key: String, accessTokenString: String, completionHandler: @escaping(Error?, [String:Any]?) -> Void)
void setAttribute(@NonNull String name, @NonNull String value, UserAttributeResponseListener listener);
void setAttribute(@NonNull String name, @NonNull String value, @NonNull AccessToken accessToken, UserAttributeResponseListener listener);

void getAttribute(@NonNull String name, UserAttributeResponseListener listener);
void getAttribute(@NonNull String name, @NonNull AccessToken accessToken, UserAttributeResponseListener listener);

void deleteAttribute(@NonNull String name, UserAttributeResponseListener listener);
void deleteAttribute(@NonNull String name, @NonNull AccessToken accessToken, UserAttributeResponseListener listener);

void getAllAttributes(@NonNull UserAttributeResponseListener listener);
void getAllAttributes(@NonNull AccessToken accessToken, @NonNull UserAttributeResponseListener listener);
const userProfileManager = require("ibmcloud-appid").UserProfileManager;
userProfileManager.init();
var accessToken = req.session[WebAppStrategy.AUTH_CONTEXT].accessToken;

// get all attributes
userProfileManager.getAllAttributes(accessToken).then(function (attributes) {

        });

// get single attribute
userProfileManager.getAttribute(accessToken, name).then(function (attributes) {

        });

// set attribute value
userProfileManager.setAttribute(accessToken, name, value).then(function (attributes) {

        });

// delete attribute
userProfileManager.deleteAttribute(accessToken, name).then(function () {

        });
//Server-side Swift example

func getAllAttributes(accessToken: String, completionHandler: (Swift.Error?, [String: Any]?) -> Void)
func getAttribute(accessToken: String, attributeName: String, completionHandler: (Swift.Error?, [String: Any]?) -> Void)
func setAttribute(accessToken: String, attributeName: String, attributeValue : "abc", completionHandler: (Swift.Error?, [String: Any]?) -> Void)
func deleteAllAttributes(accessToken: String, completionHandler: (Swift.Error?, [String: Any]?) -> Void)

カスタム属性の設定

カスタム属性を設定して、ユーザーに関する情報 (役割や好みなど) をプロファイルに追加できます。 ユーザーがアプリケーションにサインインする前にカスタム属性を設定しておく場合は、将来のユーザーの事前登録を参照してください。

デフォルトでは、カスタム属性は変更可能であり、クライアント・アプリケーションから App ID アクセス・トークンを使用して更新できます。 つまり、適切な予防措置を講じていないと、アクセス・トークンを持ったユーザーまたはアプリケーションによって、初回サインインの直後にカスタム属性を更新される可能性があります。 このため、意図しない結果が生じる可能性があります。 例えば、ユーザーが自分の役割をユーザーから管理者に変更するかもしれません。つまり、悪意のあるユーザーに管理特権が与えられることになります。

  1. App ID ダッシュボードの**「ユーザー・プロファイル」>「設定」**タブに移動します。

  2. カスタム属性を**「使用可能」**に切り替えます。

  3. APIを使用してアクセス・トークンとIDトークンを取得する。

    1. 資格情報からテナント ID、クライアント ID、シークレット、および OAuth サーバー URL を取得します。

    2. base64 エンコーダーを使用して、クライアント ID とシークレットをエンコードします。

    3. 以下のコード例を使用して、トークンを取得します。 トークンを取得するために使用する付与タイプは、使用している許可のタイプに応じて異なります。 オプションの詳細リストについては、 swaggerドキュメントを参照してください。

      curl -X POST 'https://<region>.appid.cloud.ibm.com/oauth/v4/<tenantID>/token' \
      -H 'Authorization: Basic base64Encoded{<clientID>:<clientSecret>}' \
      -H 'Accept: application/json' \
      -F 'grant_type=password' \
      -F 'username=testuser@test.com' \
      -F 'password=testuser'
      
      // iOS Swift example
      
      class delegate : TokenResponseDelegate {
         public func onAuthorizationSuccess(accessToken: AccessToken?, identityToken: IdentityToken?, refreshToken: RefreshToken?, response:Response?) {
         //User authenticated
         }
      
         public func onAuthorizationFailure(error: AuthorizationError) {
         //Exception occurred
         }
      }
      
      AppID.sharedInstance.signinWithResourceOwnerPassword(username: username, password: password, delegate: delegate())
      
      AppID.getInstance().signinWithResourceOwnerPassword(getApplicationContext(), username, password, new TokenResponseListener() {
         @Override
         public void onAuthorizationFailure (AuthorizationException exception) {
            //Exception occurred
         }
      
         @Override
         public void onAuthorizationSuccess (AccessToken accessToken, IdentityToken identityToken, RefreshToken refreshToken) {
            //User authenticated
         }
      });
      
      // Declare the API you want to protect
      app.get("/api/protected",
      
         passport.authenticate(APIStrategy.STRATEGY_NAME, {
         session: false
         }),
         function(req, res) {
         // Get full appIdAuthorizationContext from request object
         var appIdAuthContext = req.appIdAuthorizationContext;
      
         appIdAuthContext.accessToken; // Raw access_token
         appIdAuthContext.accessTokenPayload; // Decoded access_token JSON
         appIdAuthContext.identityToken; // Raw identity_token
         appIdAuthContext.identityTokenPayload; // Decoded identity_token JSON
         appIdAuthContext.refreshToken; // Raw refresh_token
         ...
         }
      );
      
      // Server-side swift example
      
      let options = [
         "clientId": "<clientID>",
         "secret": "<secret>",
         "tenantId": "<tenantID>",
         "oauthServerUrl": "<oauthServerURL>",
         "redirectUri": "<appURL>" + CALLBACK_URL
      ]
      let webappKituraCredentialsPlugin = WebAppKituraCredentialsPlugin(options: options)
      let kituraCredentials = Credentials()
      kituraCredentials.register(plugin: webappKituraCredentialsPlugin)
      
  4. attributes エンドポイントを使用して、PUT 要求を行います。

    curl -X PUT "https://<region>.appid.cloud.ibm.com/api/v1/attributes/<attributeName>" \
    -H "Authorization: Bearer <token>" \
    -d "<attributeValue>"
    
    // iOS Swift example
    
    AppID.sharedInstance.userProfileManager?.setAttribute("key", "value") { (error, result) in
       guard let result = result, error == nil else {
          return // an error has occurred
       }
    // attributes recieved as a Dictionary
    })
    
    appId.getUserProfileManager().setAttribute(name, value, useThisToken, new UserProfileResponseListener() {
       @Override
       public void onSuccess(JSONObject attributes) {
       // attributes received in JSON format on successful response
       }
    
       @Override
       public void onFailure(UserAttributesException e) {
       // exception occurred
       }
    });
    
    const userProfileManager = require("ibmcloud-appid").UserProfileManager;
    userProfileManager.init();
    
    var accessToken = req.session[WebAppStrategy.AUTH_CONTEXT].accessToken;
    
    userProfileManager.setAttribute(accessToken, name, value).then(function (attributes) {
       // attributes returned as dictionary
    });
    
    // Server-side Swift
    
    let userProfileManager = UserProfileManager(options: options)
    let accesstoken = "access token"
    
    userProfileManager.setAttribute(accessToken: accessToken, attributeName: "name", attributeValue : "abc") { (error, response) in
       guard let response = response, error == error else {
       return // an error has occurred
       }
       // attributes received as a Dictionary
    }