認証コード

認証コードとは、ユーザーが自分のログイン情報を共有することなくアプリケーションがユーザーの代わりに認証を行えるようにする許可種別です。この許可種別により、アプリケーションはユーザーを偽装することができます。

認証コード対応コネクタの開発

OAuth のサポートは、​@AuthorizationCode​ アノテーションによって ​ConnectionProvider​ レベルで追加されます。

Salesforce Connector 用の ​ConnectionProvider​ を極端に簡素化した例を示します。

@AuthorizationCode(
    authorizationUrl = "https://login.salesforce.com/services/oauth2/authorize",
    accessTokenUrl = "https://login.salesforce.com/services/oauth2/token")
public class SalesforceOAuthConnectionProvider<C> implements ConnectionProvider<SalesforceClient> {

   @Parameter
   @Optional(defaultValue = "34.0")
   private Double apiVersion;

  /**
   * Tailors the login page to the user's device type.
   */
  @OAuthParameter
  private String display;

  /**
   * Avoid interacting with the user
   */
  @OAuthParameter
  @Optional(defaultValue = "false")
  private boolean immediate;

  /**
   * Specifies how the authorization server prompts the user for reauthentication and reapproval
   */
  @OAuthParameter
  @Optional(defaultValue = "true")
  private boolean prompt;

  @OAuthCallbackValue(expression = "#[payload.instance_url]")
  private String instanceId;

  @OAuthCallbackValue(expression = "#[payload.id]")
  private String userId;

  private AuthorizationCodeState state;

  @Override
  public SalesforceClient connect() throws ConnectionException {
    if (state.getAccessToken() == null) {
      throw new SalesforceException(MessageFormat.format(COULD_NOT_EXTRACT_FIELD, "accessToken"));
    }

    if (instanceId == null) {
      throw new SalesforceException(MessageFormat.format(COULD_NOT_EXTRACT_FIELD, "instanceId"));
    }

    return new SalesforceClient(state, instanceId, apiVersion); (1)
  }

  public void disconnect(SalesforceClient connection) {
    connection.close();
  }

  @Override
  public ConnectionValidationResult validate(SalesforceClient connection) {
    return success();
  }
}
1 接続が受信するのは状態オブジェクトであり、アクセストークンではありません。アクセストークンは随時更新または認証解除される可能性があり、更新または認証解除されると接続が使用できなくなるため、アクセストークンが必要な場合は必ず状態オブジェクトを照会する必要があります。詳細は、​「各要求での状態オブジェクトの常時クエリ」​を参照してください。

SDK の他の接続と同様に、クラスは ​ConnectionProvider​ を実装しています。 オブジェクトによって認証された操作は、プロバイダーが返す ​SalesforceClient​ オブジェクトを処理します。このデザインアプローチの重要な点は、コネクタは簡素な旧式の基本認証を使用する ​ConnectionProvider​ を別に定義することもできるため、使用する認証方法と操作が切り離され、すべての操作の互換性が保証されるということです。

@AuthorizationCode

このアノテーションは、この接続プロバイダーが認証コード許可種別を使用した OAuth ダンスを必要とすることを示します。このアノテーションには以下の属性があります。

public @interface AuthorizationCode {

  /**
   * @return The Url of the endpoint which provides the access tokens
   */
  String accessTokenUrl();

  /**
   * @return The url of the authorization endpoint which starts the OAuth dance
   */
  String authorizationUrl();

  /**
   * @return Expression to be used on the response of {@link #accessTokenUrl()} to extract the access token
   */
  String accessTokenExpr() default "#[payload.access_token]";

  /**
   * @return Expression to be used on the response of {@link #accessTokenUrl()} to extract the access token expiration
   */
  String expirationExpr() default "#[payload.expires_in]";

  /**
   * @return Expression to be used on the response of {@link #accessTokenUrl()} to extract the refresh token
   */
  String refreshTokenExpr() default "#[payload.refresh_token]";

  /**
   * @return The default set of scopes to be requested, as a comma separated list. Empty string means no default scopes.
   */
  String defaultScopes() default "";
}

接続管理戦略

上記の例のプロバイダーは、​ConnectionProvider​ インターフェースの特殊化を実装していないため、OAuth メカニズムを他の接続管理戦略と組み合わせることができます。使用するインターフェース (​PoolingConnectionProvider​、​CachedConnectionProvider​、​ConnectionProvider​ など) によって、接続オブジェクトをプールまたはキャッシュしたり、毎回ゼロから作り直したりすることができます。接続管理の詳細については、​接続性のリファレンス​についての説明を参照してください。

このシナリオで標準 (汎用) ​ConnectionProvider​ インターフェースを使用する際のセマンティクスに注意してください。通常の「非 OAuth」接続プロバイダーでは、標準インターフェースを使用することは、コンポーネントが接続を必要とするたびに新しい接続が作成され、コンポーネントの終了時に破棄されることを意味します。これは OAuth のケースにも当てはまりますが、OAuth ダンスが再び実行されるという意味ではありません。OAuth でも新しい接続オブジェクトが作成されますが、同じアクセストークンが有効である間は再利用されます。

通常のパラメーターと OAuth パラメーターの違い

この ​ConnectionProvider​ は、他のすべての接続プロバイダーと同様にパラメーターを持ちます。ただし、通常のパラメーターと ​@OAuthParameter​ では概念が異なりますので注意してください。

OAuthParameter​ パラメーターは、OAuth ダンスの実行中にカスタムパラメーターとして使用されます。たとえば、接続プロバイダーが ​apiVersion​ パラメーターを使用して ​SalesforceClient​ を作成する場合、即時パラメーターは、実際にはサービスプロバイダーへの OAuth 要求で送信されます。

モジュールの視点から見ると、OAuth パラメーターもユーザーが値を提供するパラメーターの 1 つに過ぎません。これらのパラメーターには、​@Optional​、​@Expression​、および従来の ​@Parameter​ アノテーションで使用できる他のすべてのアノテーションを組み合わせることができます。DSL では、通常のパラメーターと OAuth パラメーターは一緒に表示されます。モジュールのエンドユーザーには違いが一切分かりません。

要求の別名

カスタム OAuth パラメーターには、Java でサポートされていない文字が含まれている場合があります。「Api-Key」などです。「-」は項目名には使用できないため、​@OAuthParameter​ アノテーションの ​requestAlias​ という省略可能なパラメーターで、次のように指定できます。

@OAuthParameter(requestAlias = "api-key")
private String apiKey;

@OAuthCallbackValue

サービスプロバイダーが OAuth コールバックで返す応答からは、コールバック値が抽出されます。ほとんどのサービスプロバイダーは標準項目 (アクセストークンや更新トークン、有効期限情報など) を返すだけですが、追加の項目を返すプロバイダーもあります。たとえば Salesforce の場合は、ユーザー ID とインスタンス ID を返します。

このアノテーションには、応答に適用して値を抽出するための式が含まれています。抽出された値は、接続プロバイダーが使用する項目に割り当てられます。​connect()​、​validate()​、または ​disconnect()​ メソッドを呼び出すと、項目が設定されて使用可能になります。

@AuthorizationCodeState

AuthorizationCode​ アノテーションが付加されたすべての ​ConnectionProvider​ は、​AuthorizationCodeState​ 型の項目を 1 つ (だけ) 持っている​必要があります​。

この項目は、OAuth ダンスの結果に関する情報を含む、シンプルな不変の POJO です。この POJO には以下の情報が含まれます。

public interface AuthorizationCodeState {

  /**
   * @return The obtained access token
   */
  String getAccessToken();

  /**
   * @return The obtained refresh token
   */
  Optional<String> getRefreshToken();

  /**
   * @return The id of the user that was authenticated
   */
  String getResourceOwnerId();

  /**
   * @return The access token's expiration. The actual format of it depends on the OAuth provider
   */
  Optional<String> getExpiresIn();

  /**
   * @return The OAuth state that was originally sent
   */
  Optional<String> getState();

  /**
   * @return The url of the authorization endpoint that was used in the authorization process
   */
  String getAuthorizationUrl();

  /**
   * @return The url of the access token endpoint that was used in the authorization process
   */
  String getAccessTokenUrl();

  /**
   * @return The OAuth consumer key that was used in the authorization process
   */
  String getConsumerKey();

  /**
   * @return The OAuth consumer secret that was used in the authorization process
   */
  String getConsumerSecret();

  /**
   * @return The external callback url that the user configured or {@link Optional#empty()} if none was provided
   */
  Optional<String> getExternalCallbackUrl();

  /**
   * Customizes the placement of the client credentials in the request.
   *
   * @Since 1.4
   *
   * @return the selected {@link CredentialsPlacement}. Defaults to {@link CredentialsPlacement#BODY}.
   */
  CredentialsPlacement credentialsPlacement() default BODY;

  /**
   * Whether the redirect_uri parameter should be included in the refresh token request. Defaults to {@code true}
   *
   * @since 1.4.0
   */
  boolean includeRedirectUriInRefreshTokenRequest() default true;
}

プロバイダーは、このオブジェクトを介して ​accessToken​ や OAuth ダンス中に取得した他の標準情報にアクセスします。元の Salesforce の例を見直せば、​connect()​ メソッドがこの POJO を利用してクライアントを作成していることがわかります。