속성 저장 및 액세스

IBM Cloud® App ID를 사용하면 애플리케이션의 개별 사용자에 관한 정보를 프로파일로 컴파일할 수 있습니다. 프로파일에 있는 정보는 귀하가 앱과 상호작용하는 방식을 통해 사용자에 관해 배우게 되거나 귀하가 사용자 대신 추가할 수 있습니다. 정보를 저장하면 사용자를 위해 앱의 개인화된 환경을 만드는 데 도움이 되도록 이 정보에 액세스할 수 있습니다.

프로파일 이해

사용자 프로파일은 특정 사용자에 대해 알려진 모든 정보로 하나의 JSON 오브젝트로 컴파일되고 App ID에 의해 저장됩니다. 획득하여 프로파일에 저장할 수 있는 정보 또는 속성의 유형은 predefinedcustom의 두 가지가 있습니다. 사전 정의된 속성은 사용자 ID로 국한되며, 사용자가 앱에 사인인할 때 ID 제공자에서 리턴하고, 사용자 이름 및 연령 등의 정보를 포함할 수 있습니다. 사용자 정의 속성은 사용자에 대한 추가 정보를 저장하는 데 사용됩니다. 이 속성은 본인이 직접 설정하거나 사용자가 앱과 상호작용할 때 해당 사용자에 대해 학습될 수 있습니다. 사용자 정의 속성에는 지정된 역할, 음식 선호도 또는 비행기에서 선호하는 통로 좌석 등이 포함될 수 있습니다.

caption-side=bottom"
App ID 사용자 프로필 사용자 프로필 정보 흐름

각각의 사용자에 대해 최대 100KB의 정보를 저장할 수 있습니다.

사용자 프로파일 정보를 얻으려면 어떻게 해야 합니까?

사용자 정보에 액세스할 수 있는 여러 가지 방법과 함께 해당 정보에 액세스하려는 몇 가지 다른 이유가 있습니다. 호출하기 위해 선택하는 엔드포인트는 유스 케이스에 따라 다를 수 있습니다.

API를 사용하여 작업해야 하는 경우 다음 이미지와 해당 정보를 참조하여 정보를 가져오는 방법을 확인하십시오.

caption-side=bottom"
App ID 사용자 프로필 엔드포인트 옵션 사용자 정보에 액세스하는 데 사용할 수 있는 엔드포인트 옵션

/oauth/v4/<tenantID>/token
인증에 성공하고 나면 가장 일반적인 사용자 정보(이름, 사진 또는 이메일)를 포함하는 액세스 및 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로부터 액세스 및 ID 토큰을 받습니다. 서비스에서 속성의 서브세트를 사용자의 액세스 및 ID 토큰에 자동으로 삽입합니다. 정보가 토큰에 없으면 다음 엔드포인트를 사용하여 정보를 찾을 수 있습니다.

/userinfo 엔드포인트 액세스

구성된 ID 제공자가 제공하는 사용자 정보를 확인하기 위해 사전 정의된 속성에 액세스할 수 있습니다.

  1. openid 범위를 사용하여 올바른 액세스 토큰이 있는지 확인하십시오. /introspect 엔드포인트를 사용하여 토큰이 올바른지 확인할 수 있습니다.

  2. /userinfo 엔드포인트에 요청합니다. 새 토큰이 SDK에 명시적으로 전달되지 않으면 App ID에서 마지막으로 수신된 토큰을 사용하여 응답을 검색하고 유효성을 검증합니다. 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 청구가 ID 토큰의 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를 사용하여 액세스 및 신원 토큰을 얻습니다.

    1. 인증 정보에서 테넌트 ID, 클라이언트 ID, 시크릿 및 OAuth 서버 URL을 얻으십시오.

    2. base64 인코더를 사용하여 클라이언트 ID 및 시크릿을 인코딩하십시오.

    3. 토큰을 검색하려면 코드를 사용하십시오. 토큰을 얻을 때 사용하는 권한 부여 유형은 작업 중인 권한 유형에 따라 달라질 수 있습니다. 자세한 옵션 목록은 스웨거 문서를 참조하세요.

      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
    }