アカウントリンクに向けてアクセストークンに移行する

以前は、一部のケースで、IDトークンを使ってユーザーアカウントのリンクやリンク解除ができました。この機能は、廃止されます。今後は、すべてのケースでアクセストークンを使う必要があります。

影響のある機能

アカウントリンクにおける変更点は以下のとおりです:

  • AuthorizationヘッダーでIDトークンを使用できなくなりました。代わりにアクセストークンを使用する必要があります。

  • Authorizationヘッダーでアクセストークンを使用し、付与されたアクセス許可がupdate:usersの場合、要求のボディにはセカンダリアカウントのuser_idまたはIDトークンのいずれかを送信できます。

  • Authorizationヘッダーでアクセストークンを使用し、付与されたアクセス許可がupdate:current_user_metadataの場合、要求のボディにはセカンダリアカウントのIDトークンのみ送信できます。

  • 要求の本文でセカンダリアカウントのIDトークンを送信する場合(上記2つのポイントで紹介したユースケース)、以下のような条件があります。

    • IDトークンはRS256を使用して署名される必要があります(この値は、[Dashboard]>[Clients(クライアント)]>[Client Settings(クライアントの設定)]>[Advanced Settings(高度な設定)]>[OAuth]から設定できます。

    • IDトークンのaudクレームは、クライアントを特定し、アクセストークンのazpクレームと同じ値でなければいけません。

  • Authorizationヘッダーでアカウントのリンクを解除する目的です。代わりにアクセストークンを使用しなければなりません。

アカウントをリンクする方法、リンクを解除する方法はいくつかあります。次のリストで、ユースケースと変更が及ぼす影響を確認してください。

ユースケース ステータス
Management APIのPOST /api/v2/users/{id}/identitiesエンドポイントを使用し、プライマリアカウントのIDトークンをAuthorizationヘッダーで送信する。 影響あり
Management APIのPOST /api/v2/users/{id}/identitiesエンドポイントを使用し、(update:usersスコープ付きの)アクセストークンをauthorizationヘッダーで、セカンダリアカウントのuser_idをペイロードとして送信する。 影響なし
Management APIのPOST /api/v2/users/{id}/identitiesエンドポイントを使用し、(update:current_user_identitiesスコープ付きの)アクセストークンをAuthorizationヘッダーで、セカンダリアカウントのuser_idをペイロードとして送信する。 影響あり
Management APIのPOST /api/v2/users/{id}/identitiesエンドポイントを使用し、アクセストークンをAuthorizationヘッダーで、セカンダリアカウントのIDトークンをペイロードとして送信する。 新しいユースケース
auth0.Managementのインスタンスを作成するために、auth0.jsライブラリーとプライマリアカウントのIDトークンを使用する。 影響あり
auth0.Managementのインスタンスを作成するために、auth0.jsライブラリーと(update:usersスコープ付きの)アクセストークンを使用する。 影響なし
auth0.Managementのインスタンスを作成するために、auth0.jsライブラリーと(update:current_user_identitiesスコープ付きの)アクセストークンを使用する。 影響あり
Management APIのDELETE /api/v2/users/{id}/identities/{provider}/{user_id}エンドポイントを使用し、プライマリアカウントのIDトークンをAuthorizationヘッダーで送信する。 影響あり
Management APIのDELETE /api/v2/users/{id}/identities/{provider}/{user_id}エンドポイントを使用し、アクセストークンをAuthorizationヘッダーで送信する。 影響なし

アクション

IDエンドポイントにリンクするアカウントへのすべての呼び出しを確認し、上記の脆弱なフローを利用する呼び出しを更新します。呼び出しを更新する方法には、次のようなものがあります。

  • クライアント側/ユーザー開始のリンクシナリオ: クライアント側のリンク シナリオの場合、update:current_user_identitiesスコープのアクセス トークンを使用してIdentitiesエンドポイントへの呼び出しを行い、ペイロード(link_with)でセカンダリ アカウントの ID トークンを提供します。IDトークンは、OAuth/OIDC準拠のフローを通じて取得しなければなりません。

  • サーバー側リンクシナリオ: サーバー側リンクシナリオの場合は、update:usersスコープのアクセス トークンを使用してIdentitiesエンドポイントへの呼び出しを行い、ペイロードでセカンダリ アカウントのuser_idを指定します。

詳細については、ユーザーアカウントのリンクを参照してください。

ユーザーアカウントをリンクする

ユーザーアカウントをリンクするには、Management APIユーザーアカウントのリンクエンドポイントを呼び出すか、Auth0.jsライブラリを使用します。

Management APIで現在のユーザーアカウントをリンクする

一般的なユースケースは、ログインしたユーザーに、アプリを使ってのアカウントのリンクを許可する、というものです。

非推奨になる前は、プライマリユーザーのIDトークンまたはアクセストークン(update:current_user_identitiesスコープを含む) を使用してManagement APIで認証し、ユーザーアカウントのリンクエンドポイントを使用できました。

次に、アクセストークン(update:current_user_identitiesスコープを含む)を取得し、それを使用してAPIで認証し、ユーザーアカウントのリンクエンドポイントを使用する必要があります。ペイロードは、セカンダリユーザーのIDトークンでなければなりません。

  1. 次の例に示すように、update:current_user_identitiesスコープでアクセストークンを取得します。この例では暗黙的フローを使用していますが、任意の種類のアプリケーションのアクセストークンを取得できます。

  2. 以前の、IDトークンを使う方法では、コードは次のようになります:

    codeblockOld.header.login.configureSnippet
    https://{yourDomain}/authorize?
          scope=openid
          &response_type=id_token
          &client_id={yourClientId}
          &redirect_uri=https://{yourApp}/callback
          &nonce=NONCE
          &state=OPAQUE_VALUE

    Was this helpful?

    /
    アクセストークンを使う新しい方法だと、コードは次のようになります:
    codeblockOld.header.login.configureSnippet
    https://{yourDomain}/authorize?
          audience=https://{yourDomain}/api/v2/
          &scope=update:current_user_identities
          &response_type=token%20id_token
          &client_id={yourClientId}
          &redirect_uri=https://{yourApp}/callback
          &nonce={nonce}
          &state={opaqueValue}

    Was this helpful?

    /

  3. Management APIにアクセスできるアクセストークンを取得するには以下を行います。

    1. audiencehttps://{yourDomain}/api/v2/に設定する

    2. スコープ${scope}を要求する

    3. response_typeid_token tokenに設定し、Auth0がIDトークンとアクセストークンの両方を送るようにするアクセストークンをデコードすると、次のような内容になります:

      {
            "iss": "https://{yourDomain}/",
            "sub": "auth0|5a620d29a840170a9ef43672",
            "aud": "https://{yourDomain}/api/v2/",
            "iat": 1521031317,
            "exp": 1521038517,
            "azp": "{yourClientId}",
            "scope": "${scope}"
          }

      Was this helpful?

      /
      audはテナントのAPI URI、スコープ${scope}subはログインユーザーのユーザーIDに設定されています。

  4. 次のような条件があります。

    1. セカンダリアカウントのIDトークンはRS256で署名されている必要があります。

    2. セカンダリアカウントのIDトークンのaudクレームはクライアントを識別し、要求の作成に使用されたアクセス トークンのazpクレームと同じ値を保持する必要があります。

  5. アクセストークンを取得したら、それを使ってユーザーアカウントをリンクします。この部分は同じで、要求のうち、Bearerトークンとして使用する値を除き変更はありません。応答も同じです。

    {
          "method": "POST",
          "url": "https://{yourDomain}/api/v2/users/PRIMARY_ACCOUNT_USER_ID/identities",
          "httpVersion": "HTTP/1.1",
          "headers": [{
          "name": "Authorization",
          "value": "Bearer ACCESS_TOKEN"
          },
          {
          "name": "content-type",
          "value": "application/json"
          }],
          "postData" : {
          "mimeType": "application/json",
          "text": "{\"link_with\":\"SECONDARY_ACCOUNT_ID_TOKEN\"}"
          }
        }

    Was this helpful?

    /

auth0.jsを使って現在のユーザーアカウントをリンクする

auth0.jsライブラリを使用してManagement APIにアクセスし、アカウントをリンクする場合は、おそらくユーザーのプライマリ ID の ID トークンを使用してauth0.Managementをインスタンス化し、それを使用してアカウントをリンクします。

  1. update:current_user_identitiesスコープのアクセス トークンを取得し、このトークンを使用して auth0.Managementをインスタンス化します。linkUserへの最後の呼び出しは変わりません。

  2. 以前の、IDトークンを使う方法では、コードは次のようになります:

    codeblockOld.header.login.configureSnippet
    // get an ID Token
        var webAuth = new auth0.WebAuth({
          clientID: '{yourClientId}',
          domain: '{yourDomain}',
          redirectUri: 'https://{yourApp}/callback',
          scope: 'openid',
          responseType: 'id_token'
        });
        // create a new instance
        var auth0Manage = new auth0.Management({
          domain: '{yourDomain}',
          token: '{yourIdToken}'
        });

    Was this helpful?

    /
    アクセストークンを使う新しい方法だと、コードは次のようになります:
    codeblockOld.header.login.configureSnippet
    // get an Access Token
        var webAuth = new auth0.WebAuth({
          clientID: '{yourClientId}',
          domain: '{yourDomain}',
          redirectUri: 'https://{yourApp}/callback',
          audience: 'https://{yourDomain}/api/v2/',
          scope: 'update:current_user_identities',
          responseType: 'token id_token'
        });
        // create a new instance
        var auth0Manage = new auth0.Management({
          domain: '{yourDomain}',
          token: '{yourMgmtApiAccessToken}'
        });

    Was this helpful?

    /

    1. 応答でIDトークンとアクセストークンの両方を求めます(responseType: `token id_token`)。

    2. Management APIを意図したトークンのオーディエンスとして設定します(audience: `https://{yourDomain}/api/v2/`)。

    3. 必要な許可を要求します(scope: `update:current_user_identities`)。

    4. アクセストークンを使ってManagement APIで認証します。

Management APIで任意のユーザーアカウントをリンクする

update:usersスコープを含むアカウント リンク用のアクセス トークンを取得し、要求でセカンダリーアカウントの user_idプロバイダーを送信する場合は、何も変更する必要はありません。

ただ、ここで紹介している新しいメソッドはその代わりになります。APIでの認証には引き続きupdate:usersスコープを含むアクセストークンを使用しますが、要求のペイロードでセカンダリのアカウントIDトークンを(user_idおよびプロバイダーの代わりに)送信できます。


curl --request POST \
  --url 'https://{yourDomain}/api/v2/users/PRIMARY_ACCOUNT_USER_ID/identities' \
  --header 'authorization: Bearer ACCESS_TOKEN' \
  --header 'content-type: application/json' \
  --data '{"link_with":"SECONDARY_ACCOUNT_ID_TOKEN"}'

Was this helpful?

/
var client = new RestClient("https://{yourDomain}/api/v2/users/PRIMARY_ACCOUNT_USER_ID/identities");
var request = new RestRequest(Method.POST);
request.AddHeader("authorization", "Bearer ACCESS_TOKEN");
request.AddHeader("content-type", "application/json");
request.AddParameter("application/json", "{\"link_with\":\"SECONDARY_ACCOUNT_ID_TOKEN\"}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);

Was this helpful?

/
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io/ioutil"
)

func main() {

	url := "https://{yourDomain}/api/v2/users/PRIMARY_ACCOUNT_USER_ID/identities"

	payload := strings.NewReader("{\"link_with\":\"SECONDARY_ACCOUNT_ID_TOKEN\"}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("authorization", "Bearer ACCESS_TOKEN")
	req.Header.Add("content-type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := ioutil.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}

Was this helpful?

/
HttpResponse<String> response = Unirest.post("https://{yourDomain}/api/v2/users/PRIMARY_ACCOUNT_USER_ID/identities")
  .header("authorization", "Bearer ACCESS_TOKEN")
  .header("content-type", "application/json")
  .body("{\"link_with\":\"SECONDARY_ACCOUNT_ID_TOKEN\"}")
  .asString();

Was this helpful?

/
var axios = require("axios").default;

var options = {
  method: 'POST',
  url: 'https://{yourDomain}/api/v2/users/PRIMARY_ACCOUNT_USER_ID/identities',
  headers: {authorization: 'Bearer ACCESS_TOKEN', 'content-type': 'application/json'},
  data: {link_with: 'SECONDARY_ACCOUNT_ID_TOKEN'}
};

axios.request(options).then(function (response) {
  console.log(response.data);
}).catch(function (error) {
  console.error(error);
});

Was this helpful?

/
#import <Foundation/Foundation.h>

NSDictionary *headers = @{ @"authorization": @"Bearer ACCESS_TOKEN",
                           @"content-type": @"application/json" };
NSDictionary *parameters = @{ @"link_with": @"SECONDARY_ACCOUNT_ID_TOKEN" };

NSData *postData = [NSJSONSerialization dataWithJSONObject:parameters options:0 error:nil];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://{yourDomain}/api/v2/users/PRIMARY_ACCOUNT_USER_ID/identities"]
                                                       cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                   timeoutInterval:10.0];
[request setHTTPMethod:@"POST"];
[request setAllHTTPHeaderFields:headers];
[request setHTTPBody:postData];

NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
                                            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                                if (error) {
                                                    NSLog(@"%@", error);
                                                } else {
                                                    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
                                                    NSLog(@"%@", httpResponse);
                                                }
                                            }];
[dataTask resume];

Was this helpful?

/
$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://{yourDomain}/api/v2/users/PRIMARY_ACCOUNT_USER_ID/identities",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => "{\"link_with\":\"SECONDARY_ACCOUNT_ID_TOKEN\"}",
  CURLOPT_HTTPHEADER => [
    "authorization: Bearer ACCESS_TOKEN",
    "content-type: application/json"
  ],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}

Was this helpful?

/
import http.client

conn = http.client.HTTPSConnection("")

payload = "{\"link_with\":\"SECONDARY_ACCOUNT_ID_TOKEN\"}"

headers = {
    'authorization': "Bearer ACCESS_TOKEN",
    'content-type': "application/json"
    }

conn.request("POST", "/{yourDomain}/api/v2/users/PRIMARY_ACCOUNT_USER_ID/identities", payload, headers)

res = conn.getresponse()
data = res.read()

print(data.decode("utf-8"))

Was this helpful?

/
require 'uri'
require 'net/http'
require 'openssl'

url = URI("https://{yourDomain}/api/v2/users/PRIMARY_ACCOUNT_USER_ID/identities")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Post.new(url)
request["authorization"] = 'Bearer ACCESS_TOKEN'
request["content-type"] = 'application/json'
request.body = "{\"link_with\":\"SECONDARY_ACCOUNT_ID_TOKEN\"}"

response = http.request(request)
puts response.read_body

Was this helpful?

/
import Foundation

let headers = [
  "authorization": "Bearer ACCESS_TOKEN",
  "content-type": "application/json"
]
let parameters = ["link_with": "SECONDARY_ACCOUNT_ID_TOKEN"] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://{yourDomain}/api/v2/users/PRIMARY_ACCOUNT_USER_ID/identities")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()

Was this helpful?

/

次のような条件があります。

  • セカンダリアカウントのIDトークンはRS256で署名されている必要があります。

  • セカンダリアカウントのIDトークンのaudクレームはクライアントを識別し、要求の作成に使用されたアクセス トークンのazpクレームと同じ値を保持する必要があります。

ユーザーアカウントのリンクを解除する

IDトークンを使ってアカウントのリンクを解除するには、アクセストークンを使うようコードを更新しなければなりません。

  1. まず、update:current_user_identitiesスコープのアクセストークンを取得する必要があります。

  2. 以前の、IDトークンを使う方法では、コードは次のようになります:

    codeblockOld.header.login.configureSnippet
    https://{yourDomain}/authorize?
          scope=openid
          &response_type=id_token
          &client_id={yourClientId}
          &redirect_uri=https://{yourApp}/callback
          &nonce={nonce}
          &state={opaqueValue}

    Was this helpful?

    /
    アクセストークンを使う新しい方法だと、コードは次のようになります:
    codeblockOld.header.login.configureSnippet
    https://{yourDomain}/authorize?
          audience=https://{yourDomain}/api/v2/
          &scope=update:current_user_identities
          &response_type=token%20id_token
          &client_id={yourClientId}
          &redirect_uri=https://{yourApp}/callback
          &nonce={nonce}
          &state={opaqueValue}

    Was this helpful?

    /

  3. Management APIにアクセスできるアクセストークンを取得するには以下を行います。

    1. audiencehttps://{yourDomain}/api/v2/に設定する

    2. スコープ${scope}を要求する

    3. response_typeid_token tokenに設定し、Auth0がIDトークンとアクセストークンの両方を送るようにするアクセストークンをデコードすると、次のような内容になります:

      codeblockOld.header.login.configureSnippet
      {
            "iss": "https://{yourDomain}/",
            "sub": "auth0|5a620d29a840170a9ef43672",
            "aud": "https://{yourDomain}/api/v2/",
            "iat": 1521031317,
            "exp": 1521038517,
            "azp": "{yourClientId}",
            "scope": "update:current_user_identities"
          }

      Was this helpful?

      /
      audはテナントのAPI URI、スコープupdate:current_user_identitiessubはログインユーザーのユーザーIDに設定されています。

  4. アクセストークンを取得したら、Authorizationヘッダーでそれを使用して、Management API のユーザーIDエンドポイントのリンク解除を呼び出すことができます。

  5. 以前の方法では、呼び出しは次のようになります:

    DELETE https://YOUR_DOMAIN/api/v2/users/{primaryAccountUserId}/identities/{secondaryAccountProvider}/{secondaryAccountUserId}
        Authorization: 'Bearer {yourIdTokenOrMgmtApiAccessToken}'

    Was this helpful?

    /
    新しい方法では、呼び出しは次のようになります:
    DELETE https://{yourDomain}/api/v2/users/{primaryAccountUserId}/identities/{secondaryAccountProvider}/{secondaryAccountUserId}
        Authorization: 'Bearer {yourMgmtApiAccessToken}'

    Was this helpful?

    /

セキュリティに関する考慮事項

ある特定のアカウントリンクフローに、特殊な状況において悪用される可能性のある弱点が見つかりました。実際に悪用された証拠はありませんが、予防策としてこのフローを廃止することが決まりましたので、

当該のアカウントリンクフローを使用している方は、2018年10月19日までに安全な実装へと移行するようお願いしています。このガイドでご紹介している移行パスをご利用いただけば、どの機能も失われません。

2018年10月19日以降、当該のアカウントリンクフローは無効になり、ランタイムエラーが生じます。

Authorizationヘッダーにスコープupdate:current_user_identitiesを含むトークン(IDまたはアクセストークン)を使用してPost Identitiesエンドポイントを呼び出し、ペイロードにセカンダリアカウントのuser_idを含めると、影響を受けます。その他のユースケースは影響を受けません。

もっと詳しく