儲存和存取屬性

使用 IBM Cloud® App ID,您可以將個別應用程式使用者的相關資訊編譯成設定檔。 透過使用者與您應用程式互動,或者您代表使用者進行新增,設定檔中會有使用者的相關資訊。 儲存此資訊,即可對其進行存取,以協助建立使用者的個人化應用程式體驗。

瞭解設定檔

使用者設定檔是關於特定使用者的所有已知資訊 - 編譯成一個 JSON 物件,並由 App ID 儲存。 在設定檔中可以取得和儲存兩種類型的資訊(或屬性):predefined和custom。 預先定義的屬性是使用者身分所特有,當使用者登入應用程式時會由身分提供者傳回,且可以包含例如姓名或年齡的資訊。 自訂屬性用來儲存使用者的其他相關資訊。 它們可以由您設定,或是在使用者與應用程式互動時瞭解。 自訂屬性可能包含已指派的角色、食物喜好或飛機上喜好的走道座位。

App ID使用者個人資料
使用者個人資料資訊流

您最多可以為每位使用者儲存 100 KB 的資訊。

如何取得使用者設定檔資訊?

您可以使用數種不同的方式來存取使用者資訊,以及數個您想要存取的不同原因。 根據使用案例,選擇呼叫的端點可能會有所不同。

如果您需要使用 API,請參閱下圖和對應的資訊,以瞭解如何取回資訊。

App ID使用者設定檔端點選項
可用來存取使用者資訊的端點選項

/oauth/v4/<tenantID>/token
成功鑑別後,您將收到存取記號和身分記號,其中包含最常見的使用者資訊,例如姓名、照片或電子郵件。 如果您要新增其他資訊,可以使用自訂宣告對映來配置 App ID,使其將相關資訊注入記號,然後將記號傳回給您。
/oauth/v4/<tenantID>/userinfo
如果您需要深入檢視由身分提供者傳回的使用者個人資料資訊,您可以呼叫 /userinfo 端點。 建議僅在資訊無法映射至標記的情況下使用此端點,因為它需要額外的網路呼叫。
/api/v1/attributes
如果應用程式需要讀取和更新現行已登入使用者的自訂設定檔屬性,則可以使用 /attributes 端點。 例如,使用者想要更新食品喜好設定。
/management/v4/<tenantID>/users
如果您要建置可能適用於多個使用者的管理介面或處理程序,則可以使用 App ID 管理 API。 具體而言,您可以使用 /users 端點。

使用使用者資訊的最簡單方法是利用 GUI 或 SDK。 有了這些選項,所有的 API 呼叫都會在幕後為您完成。

在執行時期存取屬性

在成功進行使用者鑑別之後,您的應用程式會從 App ID 收到存取及身分記號。 該服務會自動將部分屬性注入您的存取記號和身分記號中。 如果記號中沒有這些資訊,則可以使用下列任一端點來尋找這些資訊。

存取 /userinfo 端點

若要查看已配置之身分提供者所提供的使用者相關資訊,您可以存取預先定義的屬性。

  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 規格。

如果外部身分提供者進行變更,則您可以在使用者重新登入時取得更新過的資訊。 您的新記號會擷取最新的資料。

存取 /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 取得存取及身分記號。

    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
    }