diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index 5c1fa8960d..cc1bfc9e02 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,9 +1,463 @@
変更点
======
-4.21 (2025 年 07 月 18 日)
+4.47 (2026 年 07 月 09 日)
-----------------
+- 新しい型
+ * `CursorResponse` クラス
+
+
+4.46 (2026 年 06 月 15 日)
+--------------------------
+
+- `AuthorizationFailRequest.Reason` 列挙型
+ * `INVALID_SCOPE` を追加。
+ * `UNAUTHORIZED_CLIENT` を追加。
+ * `TEMPORARILY_UNAVAILABLE` を追加。
+
+- `TokenFailRequest.Reason` 列挙型
+ * `INVALID_CLIENT` を追加。
+ * `UNAUTHORIZED_CLIENT` を追加。
+ * `INVALID_SCOPE` を追加。
+
+
+4.45 (2026 年 05 月 18 日)
+--------------------------
+
+- `AccessRight` 列挙型
+ * `MODIFY_SERVICE`、`CREATE_CLIENT`、`ADMIN` が `USE_INTROSPECTION` を含むように修正。
+
+
+4.44 (2026 年 05 月 10 日)
+--------------------------
+
+- `AuthleteApi` インターフェース
+ * `backchannelLogoutToken(BackchannelLogoutTokenRequest)` メソッドを追加。
+ * `backchannelLogoutToken(BackchannelLogoutTokenRequest, Options)` メソッドを追加。
+
+- `Client` クラス
+ * `getBackchannelLogoutUri()` メソッドを追加。
+ * `setBackchannelLogoutUri(URI)` メソッドを追加。
+ * `isBackchannelLogoutSessionRequired()` メソッドを追加。
+ * `setBackchannelLogoutSessionRequired(boolean)` メソッドを追加。
+
+- `Service` クラス
+ * `isBackchannelLogoutSupported()` メソッドを追加。
+ * `setBackchannelLogoutSupported(boolean)` メソッドを追加。
+ * `isBackchannelLogoutSessionSupported()` メソッドを追加。
+ * `setBackchannelLogoutSessionSupported(boolean)` メソッドを追加。
+
+- 新しい型
+ * `BackchannelLogoutTokenRequest` クラス
+ * `BackchannelLogoutTokenResponse` クラス
+
+
+4.43 (2026 年 04 月 17 日)
+--------------------------
+
+- `AccessRight` 列挙型
+ * `USE_INTROSPECTION` を追加。
+ * `VIEW_AUDIT_LOG` を追加。
+
+
+4.42 (2026 年 04 月 14 日)
+--------------------------
+
+- `Service` クラス
+ * `getClientAttesterRoots()` メソッドを追加。
+ * `setClientAttesterRoots(String[])` メソッドを追加。
+ * `isClientAttesterRootsEnabled()` メソッドを追加。
+ * `setClientAttesterRootsEnabled(boolean)` メソッドを追加。
+ * `isClientAttesterRootsOnly()` メソッドを追加。
+ * `setClientAttesterRootsOnly(boolean)` メソッドを追加。
+ * `getKeyAttesterRoots()` メソッドを追加。
+ * `setKeyAttesterRoots(String[])` メソッドを追加。
+ * `isKeyAttesterRootsEnabled()` メソッドを追加。
+ * `setKeyAttesterRootsEnabled(boolean)` メソッドを追加。
+ * `isKeyAttesterRootsOnly()` メソッドを追加。
+ * `setKeyAttesterRootsOnly(boolean)` メソッドを追加。
+
+
+4.41 (2026 年 03 月 28 日)
+--------------------------
+
+- `Client` クラス
+ * `getHaipVersion()` メソッドを追加。
+ * `setHaipVersion(String)` メソッドを追加。
+
+- `Service` クラス
+ * `getHaipVersion()` メソッドを追加。
+ * `setHaipVersion(String)` メソッドを追加。
+
+
+4.40 (2026 年 03 月 24 日)
+--------------------------
+
+- `Service` クラス
+ * `getAuthorizationCodeDuration()` メソッドを追加。
+ * `setAuthorizationCodeDuration(long)` メソッドを追加。
+
+
+4.39 (2026 年 03 月 20 日)
+--------------------------
+
+- `AuthleteApi` インターフェース
+ * `attestationChallenge(AttestationChallengeRequest)` メソッドを追加。
+ * `attestationChallenge(AttestationChallengeRequest, Options)` メソッドを追加。
+
+- `BackchannelAuthenticationResponse` クラス
+ * `getAttestationChallenge()` メソッドを追加。
+ * `setAttestationChallenge(String)` メソッドを追加。
+
+- `DeviceAuthorizationResponse` クラス
+ * `getAttestationChallenge()` メソッドを追加。
+ * `setAttestationChallenge(String)` メソッドを追加。
+
+- `ErrorCode` 列挙型
+ * `use_attestation_challenge` を追加。
+ * `use_fresh_attestation` を追加。
+ * `invalid_client_attestation` を追加。
+
+- `PushedAuthReqResponse` クラス
+ * `getAttestationChallenge()` メソッドを追加。
+ * `setAttestationChallenge(String)` メソッドを追加。
+
+- `RevocationResponse` クラス
+ * `getAttestationChallenge()` メソッドを追加。
+ * `setAttestationChallenge(String)` メソッドを追加。
+
+- `Service` クラス
+ * `getChallengeEndpoint()` メソッドを追加。
+ * `setChallengeEndpoint(URI)` メソッドを追加。
+ * `getAttestationChallengeTimeWindow()` メソッドを追加。
+ * `setAttestationChallengeTimeWindow(long)` メソッドを追加。
+
+- `TokenResponse` クラス
+ * `getAttestationChallenge()` メソッドを追加。
+ * `setAttestationChallenge(String)` メソッドを追加。
+
+- 新しい型
+ * `AttestationChallengeRequest` クラス
+ * `AttestationChallengeResponse` クラス
+
+
+4.38 (2026 年 03 月 04 日)
+--------------------------
+
+- `TokenUpdateRequest` クラス
+ * `getRefreshToken()` メソッドを追加。
+ * `setRefreshToken(String)` メソッドを追加。
+ * `getRefreshTokenHash()` メソッドを追加。
+ * `setRefreshTokenHash(String)` メソッドを追加。
+
+
+4.37 (2026 年 03 月 02 日)
+--------------------------
+
+- `ClientAssertionType` 列挙型
+ * `JWT_SPIFFE` を追加。
+
+
+4.36 (2026 年 02 月 26 日)
+--------------------------
+
+- `ClientAuthMethod` 列挙型
+ * `SPIFFE_JWT` を追加。
+
+- `Client` クラス
+ * `getSpiffeId()` メソッドを追加。
+ * `setSpiffeId(URI)` メソッドを追加。
+ * `getSpiffeBundleEndpoint()` メソッドを追加。
+ * `setSpiffeBundleEndpoint(URI)` メソッドを追加。
+
+
+4.35 (2026 年 01 月 10 日)
+--------------------------
+
+- `CredentialDeferredIssueRequest` クラス
+ * `isDenied()` メソッドを追加。
+ * `setDenied(boolean)` メソッドを追加。
+ * `getInterval()` メソッドを追加。
+ * `setInterval(int)` メソッドを追加。
+
+- `CredentialDeferredIssueResponse.Action` 列挙型
+ * `ACCEPTED` を追加。
+ * `ACCEPTED_JWT` を追加。
+
+- `CredentialSingleIssueRequest` クラス
+ * `isDenied()` メソッドを追加。
+ * `setDenied(boolean)` メソッドを追加。
+ * `getInterval()` メソッドを追加。
+ * `setInterval(int)` メソッドを追加。
+
+
+4.34 (2026 年 01 月 03 日)
+--------------------------
+
+- `AuthzDetailsElement` クラス
+ * デフォルトコンストラクタを明示的に追加。
+ * コピーコンストラクタ (`AuthzDetailsElement(AuthzDetailsElement)`) を追加。
+
+- `CredentialRequestInfo` クラス
+ * `getCredentialConfigurationId()` メソッドを追加。
+ * `setCredentialConfigurationId(String)` メソッドを追加。
+ * `getCredentialIdentifier()` メソッドを追加。
+ * `setCredentialIdentifier(String)` メソッドを追加。
+
+
+4.33 (2025 年 12 月 13 日)
+--------------------------
+
+- `Service` クラス
+ * `isCimdMetadataPolicyEnabled()` メソッドを追加。
+ * `setCimdMetadataPolicyEnabled(boolean)` メソッドを追加。
+ * `getCimdMetadataPolicy()` メソッドを追加。
+ * `setCimdMetadataPolicy(String)` メソッドを追加。
+
+
+4.32 (2025 年 12 月 01 日)
+--------------------------
+
+- `Service` クラス
+ * `isHttpAliasProhibited()` メソッドを追加。
+ * `setHttpAliasProhibited(boolean)` メソッドを追加。
+
+
+4.31 (2025 年 11 月 25 日)
+--------------------------
+
+- `Service` クラス
+ * `isCimdAllowlistEnabled()` メソッドを追加。
+ * `setCimdAllowlistEnabled(boolean)` メソッドを追加。
+ * `getCimdAllowlist()` メソッドを追加。
+ * `setCimdAllowlist(String[])` メソッドを追加。
+ * `isCimdWhitelistEnabled()` メソッドを削除。
+ * `setCimdWhitelistEnabled(boolean)` メソッドを削除。
+ * `getCimdWhitelist()` メソッドを削除。
+ * `setCimdWhitelist(String[])` メソッドを削除。
+
+
+4.30 (2025 年 11 月 20 日)
+--------------------------
+
+- `AuthorizationRequest` クラス
+ * `getCimdOptions()` メソッドを追加。
+ * `setCimdOptions(CimdOptions)` メソッドを追加。
+
+- `BackchannelAuthenticationRequest` クラス
+ * `getCimdOptions()` メソッドを追加。
+ * `setCimdOptions(CimdOptions)` メソッドを追加。
+
+- `DeviceAuthorizationRequest` クラス
+ * `getCimdOptions()` メソッドを追加。
+ * `setCimdOptions(CimdOptions)` メソッドを追加。
+
+- `Service` クラス
+ * `isCimdWhitelistEnabled()` メソッドを追加。
+ * `setCimdWhitelistEnabled(boolean)` メソッドを追加。
+ * `getCimdWhitelist()` メソッドを追加。
+ * `setCimdWhitelist(String[])` メソッドを追加。
+ * `isCimdAlwaysRetrieved()` メソッドを追加。
+ * `setCimdAlwaysRetrieved(boolean)` メソッドを追加。
+ * `isCimdHttpPermitted()` メソッドを追加。
+ * `setCimdHttpPermitted(boolean)` メソッドを追加。
+ * `isCimdQueryPermitted()` メソッドを追加。
+ * `setCimdQueryPermitted(boolean)` メソッドを追加。
+
+- `TokenRequest` クラス
+ * `getCimdOptions()` メソッドを追加。
+ * `setCimdOptions(CimdOptions)` メソッドを追加。
+
+- 新しい型
+ * `CimdOptions` クラス
+
+
+4.29 (2025 年 11 月 18 日)
+--------------------------
+
+- `AuthorizationResponse` クラス
+ * `isMetadataDocumentUsed()` メソッドを追加。
+ * `setMetadataDocumentUsed(boolean)` メソッドを追加。
+
+- `BackchannelAuthenticationCompleteResponse` クラス
+ * `getMetadataDocumentLocation()` メソッドを追加。
+ * `setMetadataDocumentLocation(URI)` メソッドを追加。
+ * `isMetadataDocumentUsed()` メソッドを追加。
+ * `setMetadataDocumentUsed(boolean)` メソッドを追加。
+
+- `BackchannelAuthenticationResponse` クラス
+ * `getMetadataDocumentLocation()` メソッドを追加。
+ * `setMetadataDocumentLocation(URI)` メソッドを追加。
+ * `isMetadataDocumentUsed()` メソッドを追加。
+ * `setMetadataDocumentUsed(boolean)` メソッドを追加。
+
+- `Client` クラス
+ * `getMetadataDocumentLocation()` メソッドを追加。
+ * `setMetadataDocumentLocation(URI)` メソッドを追加。
+ * `getMetadataDocumentExpiresAt()` メソッドを追加。
+ * `setMetadataDocumentExpiresAt(long)` メソッドを追加。
+ * `getMetadataDocumentUpdatedAt()` メソッドを追加。
+ * `setMetadataDocumentUpdatedAt(long)` メソッドを追加。
+ * `isDiscoveredByMetadataDocument()` メソッドを追加。
+ * `setDiscoveredByMetadataDocument(boolean)` メソッドを追加。
+ * `getClientSource()` メソッドを追加。
+ * `setClientSource(ClientSource)` メソッドを追加。
+ * `updateClientSource()` メソッドを追加。
+
+- `ClientMetadataControl` クラス
+ * `isMetadataDocumentLocationPreferred()` メソッドを追加。
+ * `setMetadataDocumentLocationPreferred(boolean)` メソッドを追加。
+
+- `DeviceAuthorizationResponse` クラス
+ * `getMetadataDocumentLocation()` メソッドを追加。
+ * `setMetadataDocumentLocation(URI)` メソッドを追加。
+ * `isMetadataDocumentUsed()` メソッドを追加。
+ * `setMetadataDocumentUsed(boolean)` メソッドを追加。
+
+- `DeviceVerificationResponse` クラス
+ * `getMetadataDocumentLocation()` メソッドを追加。
+ * `setMetadataDocumentLocation(URI)` メソッドを追加。
+ * `isMetadataDocumentUsed()` メソッドを追加。
+ * `setMetadataDocumentUsed(boolean)` メソッドを追加。
+
+- `IntrospectionResponse` クラス
+ * `getMetadataDocumentLocation()` メソッドを追加。
+ * `setMetadataDocumentLocation(URI)` メソッドを追加。
+ * `isMetadataDocumentUsed()` メソッドを追加。
+ * `setMetadataDocumentUsed(boolean)` メソッドを追加。
+
+- `Service` クラス
+ * `isClientIdMetadataDocumentSupported()` メソッドを追加。
+ * `setClientIdMetadataDocumentSupported(boolean)` メソッドを追加。
+
+- `TokenCreateRequest` クラス
+ * `isMetadataDocumentUsed()` メソッドを追加。
+ * `setMetadataDocumentUsed(boolean)` メソッドを追加。
+
+- `TokenInfo` クラス
+ * `getMetadataDocumentLocation()` メソッドを追加。
+ * `setMetadataDocumentLocation(URI)` メソッドを追加。
+ * `isMetadataDocumentUsed()` メソッドを追加。
+ * `setMetadataDocumentUsed(boolean)` メソッドを追加。
+
+- `TokenIssueResponse` クラス
+ * `getMetadataDocumentLocation()` メソッドを追加。
+ * `setMetadataDocumentLocation(URI)` メソッドを追加。
+ * `isMetadataDocumentUsed()` メソッドを追加。
+ * `setMetadataDocumentUsed(boolean)` メソッドを追加。
+
+- `TokenResponse` クラス
+ * `getMetadataDocumentLocation()` メソッドを追加。
+ * `setMetadataDocumentLocation(URI)` メソッドを追加。
+ * `isMetadataDocumentUsed()` メソッドを追加。
+ * `setMetadataDocumentUsed(boolean)` メソッドを追加。
+
+- `UserInfoResponse` クラス
+ * `getMetadataDocumentLocation()` メソッドを追加。
+ * `setMetadataDocumentLocation(URI)` メソッドを追加。
+ * `isMetadataDocumentUsed()` メソッドを追加。
+ * `setMetadataDocumentUsed(boolean)` メソッドを追加。
+
+- 新しい型
+ * `ClientSource` 列挙型
+
+
+4.28 (2025 年 11 月 05 日)
+--------------------------
+
+- `ErrorCode` 列挙型
+ * `unknown_credential_configuration` を追加。
+ * `unknown_credential_identifier` を追加。
+ * `invalid_nonce` を追加。
+ * `credential_request_denied` を追加。
+ * `invalid_notification_id` を追加。
+ * `invalid_notification_request` を追加。
+
+
+4.27 (2025 年 11 月 04 日)
+--------------------------
+
+- `AuthleteApi` インターフェース
+ * `credentialNonce(CredentialNonceRequest)` メソッドを追加。
+ * `credentialNonce(CredentialNonceRequest, Options)` メソッドを追加。
+
+- 新しい型
+ * `CredentialNonceRequest` クラス
+ * `CredentialNonceResponse` クラス
+
+- その他
+ * PR #143: fix spotbugs-annotations dependency
+
+
+4.26 (2025 年 11 月 03 日)
+--------------------------
+
+- `CredentialIssuerMetadata` クラス
+ * `getNotificationEndpoint()` メソッドを追加。
+ * `setNotificationEndpoint(URI)` メソッドを追加。
+ * `getNonceEndpoint()` メソッドを追加。
+ * `setNonceEndpoint(URI)` メソッドを追加。
+ * `getCredentialRequestEncryptionJwks()` メソッドを追加。
+ * `setCredentialRequestEncryptionJwks(String)` メソッドを追加。
+ * `getCredentialRequestEncryptionEncValuesSupported()` メソッドを追加。
+ * `setCredentialRequestEncryptionEncValuesSupported(JWEEnc[])` メソッドを追加。
+ * `getCredentialRequestEncryptionZipValuesSupported()` メソッドを追加。
+ * `setCredentialRequestEncryptionZipValuesSupported(JWEEnc[])` メソッドを追加。
+ * `isRequireCredentialRequestEncryption()` メソッドを追加。
+ * `setRequireCredentialRequestEncryption(boolean)` メソッドを追加。
+ * `getCredentialResponseEncryptionZipValuesSupported()` メソッドを追加。
+ * `setCredentialResponseEncryptionZipValuesSupported(JWEEnc[])` メソッドを追加。
+ * `getBatchSize()` メソッドを追加。
+ * `setBatchSize(int)` メソッドを追加。
+ * `getDisplay()` メソッドを追加。
+ * `setDisplay(String)` メソッドを追加。
+
+- 新しい型
+ * `JWEZip` 列挙型
+
+
+4.25 (2025 年 10 月 30 日)
+--------------------------
+
+- `Service` クラス
+ * `getOid4vciVersion()` メソッドを追加。
+ * `setOid4vciVersion(String)` メソッドを追加。
+
+
+4.24 (2025 年 10 月 06 日)
+--------------------------
+
+- `TokenResponse` クラス
+ * `getAdditionalClaims()` メソッドを追加
+ * `setAdditionalClaims(String)` メソッドを追加
+ * カスタムクレームを処理するために Native SSO のトークンレスポンスに追加されたクレームへのアクセスをサポート
+
+
+4.23 (2025 年 10 月 03 日)
+--------------------------
+
+- `ApiResponse` クラス
+ * `getResponseHeaders()` メソッドを追加。
+ * `setResponseHeaders(Map
+ * The Authlete API is supposed to be used from within the implementation of
+ * the challenge endpoint of the authorization server.
+ *
+ * The Authlete API is supposed to be used from within the implementation
+ * the challenge endpoint of the authorization server.
+ *
+ * Authlete's {@code /attestation/challenge} API returns JSON which can be
+ * mapped to this class. The authorization server implementation should
+ * retrieve the value of the
+ * When the value of the
+ *
+ * https://authlete.github.io/authlete-java-common/
- https://authlete.github.io/authlete-java-common/index.html?overview-summary.html [FRAMES]
-
説明
----
@@ -364,12 +362,21 @@ settings.setReadTimeout(5000);
- `credentialBatchIssue(CredentialBatchIssueRequest)`
- `credentialDeferredParse(CredentialDeferredParseRequest)`
- `credentialDeferredIssue(CredentialDeferredIssueRequest)`
+ - `credentialNonce(CredentialNonceRequest)`
22. OpenID Connect Native SSO for Mobile Apps 1.0 に関するメソッド群
- `nativeSso(NativeSsoRequest)`
- `nativeSsoLogout(NativeSsoLogoutRequest)`
+ 23. OAuth 2.0 Attestation-Based Client Authentication に関するメソッド群
+
+ - `attestationChallenge(AttestationChallengeRequest)`
+
+ 24. OpenID Connect Back-Channel Logout 1.0 に関するメソッド群
+
+ - `backchannelLogoutToken(BackchannelLogoutTokenRequest)`
+
*例*
次のコードは既存のサービスのリストを取得する例です。
diff --git a/README.md b/README.md
index 0fc3e0dd7f..79f92da732 100644
--- a/README.md
+++ b/README.md
@@ -55,8 +55,6 @@ JavaDoc
https://authlete.github.io/authlete-java-common/
- https://authlete.github.io/authlete-java-common/index.html?overview-summary.html [FRAMES]
-
Description
-----------
@@ -375,12 +373,21 @@ Methods in `AuthleteApi` interface can be divided into some categories.
- `credentialBatchIssue(CredentialBatchIssueRequest)`
- `credentialDeferredParse(CredentialDeferredParseRequest)`
- `credentialDeferredIssue(CredentialDeferredIssueRequest)`
+ - `credentialNonce(CredentialNonceRequest)`
22. Methods for OpenID Connect Native SSO for Mobile Apps 1.0
- `nativeSso(NativeSsoRequest)`
- `nativeSsoLogout(NativeSsoLogoutRequest)`
+ 23. Methods for OAuth 2.0 Attestation-Based Client Authentication
+
+ - `attestationChallenge(AttestationChallengeRequest)`
+
+ 24. Methods for OpenID Connect Back-Channel Logout 1.0
+
+ - `backchannelLogoutToken(BackchannelLogoutTokenRequest)`
+
*Examples*
The following code snippet is an example to get the list of your existing
diff --git a/pom.xml b/pom.xml
index f23eb457cc..d7d6f96709 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
{@link #getAction() action}
+ * response parameter from the API response and take the following steps
+ * according to the value.
+ *
+ *
+ * OK{@link #getAction() action} response
+ * parameter is {@link Action#OK OK}, it means that Authlete
+ * could prepare a challenge response successfully.
+ * {@link #getResponseContent() responseContent} response
+ * parameter.
+ *
+ * The implementation of the challenge endpoint can construct an HTTP response + * by doing like below. + *
+ * + *
+ * 200 OK
+ * Content-Type: application/json
+ * Cache-Control: no-store
+ * (Other HTTP headers)
+ *
+ * (the value of the {@link #getResponseContent() responseContent} response parameter)
+ *
+ * + * Note that the OAuth 2.0 Attestation-Based Client Authentication specification + * requires the response to include the {@code Cache-Control} header including + * the value {@code no-store}. + *
+ * + *INTERNAL_SERVER_ERROR
+ * When the value of the {@link #getAction() action} response
+ * parameter is {@link Action#INTERNAL_SERVER_ERROR INTERNAL_SERVER_ERROR},
+ * it means that an unexpected error has occurred on Authlete side or the
+ * service has not been set up properly yet.
+ *
{@link #getResponseContent() responseContent} response parameter.
+ *
+ *
+ * + * Such simple implementation of the challenge endpoint can construct an HTTP + * response by doing like below. + *
+ * + *
+ * 500 Internal Server Error
+ * Content-Type: application/json
+ * (Other HTTP headers)
+ *
+ * (the value of the {@link #getResponseContent() responseContent} response parameter)
+ *
+ * + * However, in real commercial deployments, it is rare for an authorization + * server to return "{@code 500 Internal Server Error}" when it encounters an + * unexpected internal error. It's up to implementations of authorization + * servers what they actually return in the case of internal server error. + *
+ * + * @since 4.39 + * @since Authlete 3.0.28 + * + * @see + * OAuth 2.0 Attestation-Based Client Authentication + */ +public class AttestationChallengeResponse extends ApiResponse +{ + private static final long serialVersionUID = 1L; + + + /** + * The next action that the implementation of the challenge endpoint + * should take after getting a response from Authlete's + * {@code /attestation/challenge} API. + * + * @since 4.39 + * @since Authlete 3.0.28 + */ + public enum Action + { + /** + * A challenge response has been prepared successfully. The + * implementation of the challenge endpoint should return an HTTP + * response with the HTTP status code "{@code 200 OK}" and the content + * type "{@code application/json}". + */ + OK, + + /** + * An unexpected error occurred on Authlete side or the service has + * not been set up properly yet. A simple implementation of the + * challenge endpoint would return an HTTP response with the HTTP + * status code "{@code 500 Internal Server Error}" and the content + * type "{@code application/json}". + */ + INTERNAL_SERVER_ERROR, + } + + + private Action action; + private String responseContent; + private String attestationChallenge; + + + /** + * Get the next action that the implementation of the challenge endpoint + * should take after getting a response from Authlete's + * {@code /attestation/challenge} API. + * + * @return + * The next action. + */ + public Action getAction() + { + return action; + } + + + /** + * Set the next action that the implementation of the challenge endpoint + * should take after getting a response from Authlete's + * {@code /attestation/challenge} API. + * + * @param action + * The next action. + * + * @return + * {@code this} object. + */ + public AttestationChallengeResponse setAction(Action action) + { + this.action = action; + + return this; + } + + + /** + * Get the content that the implementation of the challenge endpoint should + * use when it constructs a response. + * + * @return + * The response content in the JSON format. + */ + public String getResponseContent() + { + return responseContent; + } + + + /** + * Set the content that the implementation of the challenge endpoint should + * use when it constructs a response. + * + * @param content + * The response content in the JSON format. + * + * @return + * {@code this} object. + */ + public AttestationChallengeResponse setResponseContent(String content) + { + this.responseContent = content; + + return this; + } + + + /** + * Get the {@code attestation_challenge} value generated by the + * {@code /attestation/challenge} API. + * + * @return + * The generated {@code attestation_challenge} value. + */ + public String getAttestationChallenge() + { + return attestationChallenge; + } + + + /** + * Set the {@code attestation_challenge} value generated by the + * {@code /attestation/challenge} API. + * + * @param challenge + * The generated {@code attestation_challenge} value. + * + * @return + * {@code this} object. + */ + public AttestationChallengeResponse setAttestationChallenge(String challenge) + { + this.attestationChallenge = challenge; + + return this; + } +} diff --git a/src/main/java/com/authlete/common/dto/AuthorizationFailRequest.java b/src/main/java/com/authlete/common/dto/AuthorizationFailRequest.java index 4a690ba4a0..47b7ae2028 100644 --- a/src/main/java/com/authlete/common/dto/AuthorizationFailRequest.java +++ b/src/main/java/com/authlete/common/dto/AuthorizationFailRequest.java @@ -261,6 +261,40 @@ public enum Reason * @since 2.62 */ INVALID_TARGET, + + /** + * The requested scope is invalid, unknown, or malformed. + * + *+ * Using this reason will result in {@code error=invalid_scope}. + *
+ * + * @since 4.46 + */ + INVALID_SCOPE, + + /** + * The client is not authorized to request an authorization code using this method. + * + *+ * Using this reason will result in {@code error=unauthorized_client}. + *
+ * @since 4.46 + */ + UNAUTHORIZED_CLIENT, + + /** + * The authorization server is currently unable to handle the request due to a temporary overloading or maintenance + * of the server. (This error code is needed because a 503 Service Unavailable HTTP status code cannot be returned + * to the client via an HTTP redirect.) + * + *+ * Using this reason will result in {@code error=temporarily_unavailable}. + *
+ * + * @since 4.46 + */ + TEMPORARILY_UNAVAILABLE, } diff --git a/src/main/java/com/authlete/common/dto/AuthorizationIssueRequest.java b/src/main/java/com/authlete/common/dto/AuthorizationIssueRequest.java index 2a298dfc95..24ec702ad6 100644 --- a/src/main/java/com/authlete/common/dto/AuthorizationIssueRequest.java +++ b/src/main/java/com/authlete/common/dto/AuthorizationIssueRequest.java @@ -1,11 +1,11 @@ /* - * Copyright (C) 2014-2025 Authlete, Inc. + * Copyright (C) 2014-2026 Authlete, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -45,9 +45,9 @@ * is required unless the authorization request has come with * {@code response_type=none} (which means the client application * did not request any token to be returned). See "4. None Response Type" in OAuth 2.0 Multiple Response Type Encoding Practices for * details about {@code response_type=none}. * @@ -73,7 +73,7 @@ ** The claims of the end-user (= pieces of information about the * end-user) in JSON format. See - * OpenID Connect Core 1.0, 5.1. Standard Claims for details * about the format. *
@@ -122,7 +122,7 @@ * If you add"offline_access" scope although it is not
* included in the original request, keep in mind that the specification
* requires explicit consent from the user for the scope (OpenID Connect Core 1.0, 11. Offline Access). When
* "offline_access" is included in the original request, the
* current implementation of Authlete's {@code /auth/authorization} API
@@ -234,7 +234,7 @@
*
* @see AuthorizationResponse
*
- * @see OpenID Connect Core 1.0, 5.1. Standard Claims
*
* @author Takahiko Kawasaki
@@ -659,7 +659,7 @@ public String getClaims()
*
*
* - * See OpenID Connect Core 1.0, 5.1. Standard Claims for further details * about the format. *
@@ -670,7 +670,7 @@ public String getClaims() * @return * {@code this} object. * - * @see OpenID Connect Core 1.0, 5.1. Standard Claims */ public AuthorizationIssueRequest setClaims(String claims) @@ -750,7 +750,7 @@ public Property[] getProperties() * Keys listed below should not be used and they would be ignored on * the server side even if they were used. It's because they are reserved * in RFC 6749 and - * OpenID Connect Core 1.0. * * @@ -835,7 +835,7 @@ public String[] getScopes() * If you add"offline_access" scope although it is not
* included in the original request, keep in mind that the specification
* requires explicit consent from the user for the scope (OpenID Connect Core 1.0, 11. Offline Access). When
* "offline_access" is included in the original request, the
* current implementation of Authlete's /api/auth/authorization API checks
@@ -1657,7 +1657,7 @@ public AuthorizationIssueRequest setAccessTokenDuration(long duration)
* claim.
*
* - * This parameter needs to be provided only if you want to support the + * This parameter needs to be provided if you want to support the * "OpenID Connect Native SSO for Mobile Apps 1.0" specification * (a.k.a. "Native SSO"). To enable support for the Native SSO specification, @@ -1669,10 +1669,28 @@ public AuthorizationIssueRequest setAccessTokenDuration(long duration) * NOTE: When the response from the {@code /auth/authorization} API * contains the {@code nativeSsoRequested} property with a value of * {@code true} (see {@link AuthorizationResponse#isNativeSsoRequested()}), - * the {@code sessionId} request parameter must be provided to the + * this {@code sessionId} request parameter must be provided to the * {@code /auth/authorization/issue} API. *
* + *
+ * Also, if your server's
+ * backchannel_ metadata
+ * parameter is set to {@code true} (see
+ * {@link Service#isBackchannelLogoutSessionSupported()}), this
+ * {@code sessionId} request parameter should be provided. See the OpenID
+ * Connect Back-Channel Logout 1.0 specification for the metadata
+ * parameter.
+ *
+ * In the current Authlete implementation, this {@code sessionId} request
+ * parameter is recognized only when Native SSO is requested or the
+ * backchannel_ server
+ * metadata parameter is set to {@code true}.
+ *
- * This parameter needs to be provided only if you want to support the + * This parameter needs to be provided if you want to support the * "OpenID Connect Native SSO for Mobile Apps 1.0" specification * (a.k.a. "Native SSO"). To enable support for the Native SSO specification, @@ -1708,10 +1730,28 @@ public String getSessionId() * NOTE: When the response from the {@code /auth/authorization} API * contains the {@code nativeSsoRequested} property with a value of * {@code true} (see {@link AuthorizationResponse#isNativeSsoRequested()}), - * the {@code sessionId} request parameter must be provided to the + * this {@code sessionId} request parameter must be provided to the * {@code /auth/authorization/issue} API. *
* + *
+ * Also, if your server's
+ * backchannel_ metadata
+ * parameter is set to {@code true} (see
+ * {@link Service#isBackchannelLogoutSessionSupported()}), this
+ * {@code sessionId} request parameter should be provided. See the OpenID
+ * Connect Back-Channel Logout 1.0 specification for the metadata
+ * parameter.
+ *
+ * In the current Authlete implementation, this {@code sessionId} request
+ * parameter is recognized only when Native SSO is requested or the
+ * backchannel_ server
+ * metadata parameter is set to {@code true}.
+ *
+ * This can happen when the service supports OAuth Client ID Metadata Document. + * (cf. {@link Service#isClientIdMetadataDocumentSupported()}) + *
+ * + *+ * When this flag is {@code true}, {@code client.}{@link + * Client#getMetadataDocumentLocation() getMetadataDocumentLocation()} + * returns the location of the client's metadata document. + *
+ * + * @return + * {@code true} if the value of the {@code client_id} request + * parameter is the location of the client's metadata document. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public boolean isMetadataDocumentUsed() + { + return metadataDocumentUsed; + } + + + /** + * Set the flag which indicates whether the value of the {@code client_id} + * request parameter included in the authorization request is the location + * of the client's metadata document. + * + *+ * This can happen when the service supports OAuth Client ID Metadata Document. + * (cf. {@link Service#isClientIdMetadataDocumentSupported()}) + *
+ * + *+ * When this flag is {@code true}, {@code client.}{@link + * Client#getMetadataDocumentLocation() getMetadataDocumentLocation()} + * returns the location of the client's metadata document. + *
+ * + * @param used + * {@code true} to indicate that the value of the {@code client_id} + * request parameter is the location of the client's metadata + * document. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public void setMetadataDocumentUsed(boolean used) + { + metadataDocumentUsed = used; + } + + /** * Get the list of ACRs (Authentication Context Class References) * requested by the client application. The value come from (1) diff --git a/src/main/java/com/authlete/common/dto/AuthzDetailsElement.java b/src/main/java/com/authlete/common/dto/AuthzDetailsElement.java index be8b29d4d9..f69bf8ab8b 100644 --- a/src/main/java/com/authlete/common/dto/AuthzDetailsElement.java +++ b/src/main/java/com/authlete/common/dto/AuthzDetailsElement.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2021 Authlete, Inc. + * Copyright (C) 2019-2026 Authlete, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,7 +30,7 @@ */ public class AuthzDetailsElement implements Serializable { - private static final long serialVersionUID = 4L; + private static final long serialVersionUID = 5L; private String type; @@ -42,6 +42,43 @@ public class AuthzDetailsElement implements Serializable private String otherFields; + /** + * The default constructor. + */ + public AuthzDetailsElement() + { + } + + + /** + * The copy constructor. + * + * @param element + * The source instance. + * + * @since 4.34 + */ + public AuthzDetailsElement(AuthzDetailsElement element) + { + if (element != null) + { + this.type = element.type; + this.locations = copy(element.locations); + this.actions = copy(element.actions); + this.dataTypes = copy(element.dataTypes); + this.identifier = element.identifier; + this.privileges = copy(element.privileges); + this.otherFields = element.otherFields; + } + } + + + private static String[] copy(String[] source) + { + return (source != null) ? source.clone() : null; + } + + /** * Get the type of this element. * diff --git a/src/main/java/com/authlete/common/dto/BackchannelAuthenticationCompleteResponse.java b/src/main/java/com/authlete/common/dto/BackchannelAuthenticationCompleteResponse.java index 67326120b9..c4bb60e36a 100644 --- a/src/main/java/com/authlete/common/dto/BackchannelAuthenticationCompleteResponse.java +++ b/src/main/java/com/authlete/common/dto/BackchannelAuthenticationCompleteResponse.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2023 Authlete, Inc. + * Copyright (C) 2018-2025 Authlete, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -139,7 +139,7 @@ */ public class BackchannelAuthenticationCompleteResponse extends ApiResponse { - private static final long serialVersionUID = 8L; + private static final long serialVersionUID = 9L; /** @@ -218,6 +218,22 @@ public enum Action */ private boolean clientEntityIdUsed; + /** + * The location of the client's metadata document. + * + * @since 4.29 + * @since Authlete 3.0.22 + */ + private URI metadataDocumentLocation; + + /** + * Whether the client's metadata document was used. + * + * @since 4.29 + * @since Authlete 3.0.22 + */ + private boolean metadataDocumentUsed; + /** * @since Authlete 2.0.0 */ @@ -581,18 +597,129 @@ public BackchannelAuthenticationCompleteResponse setClientEntityIdUsed(boolean u /** - * Get the client identifier used in the backchannel authentication - * request. + * Get the location of the client's metadata document. This property + * holds a non-null value only when {@link #isMetadataDocumentUsed()} + * returns {@code true}. + * + * @return + * The location of the client's metadata document. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public URI getMetadataDocumentLocation() + { + return metadataDocumentLocation; + } + + + /** + * Set the location of the client's metadata document. This property + * should hold a non-null value only when {@link #isMetadataDocumentUsed()} + * returns {@code true}. + * + * @param location + * The location of the client's metadata document. + * + * @return + * {@code this} object. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public BackchannelAuthenticationCompleteResponse setMetadataDocumentLocation(URI location) + { + this.metadataDocumentLocation = location; + + return this; + } + + + /** + * Get the flag which indicates whether the location of the client's + * metadata document was used as a client ID. + * + *+ * This can happen when the service supports OAuth Client ID Metadata Document. + * (cf. {@link Service#isClientIdMetadataDocumentSupported()}) + *
+ * + * @return + * {@code true} if the location of client's metadata document + * was used as a client ID. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public boolean isMetadataDocumentUsed() + { + return metadataDocumentUsed; + } + + + /** + * Set the flag which indicates whether the location of the client's + * metadata document was used as a client ID. * *- * When {@link #isClientIdAliasUsed()} returns {@code true}, this method - * returns the same value as {@link #getClientIdAlias()} does. Otherwise, - * if {@link #isClientEntityIdUsed()} returns {@code true}, this method - * returns the same value as {@link #getClientEntityId()}{@code .toString()} - * does. In other cases, this method returns the string representation of - * the value returned from {@link #getClientId()}. + * This can happen when the service supports OAuth Client ID Metadata Document. + * (cf. {@link Service#isClientIdMetadataDocumentSupported()}) *
* + * @param used + * {@code true} to indicate that the location of the client's + * metadata document was used as a client ID. + * + * @return + * {@code this} object. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public BackchannelAuthenticationCompleteResponse setMetadataDocumentUsed(boolean used) + { + this.metadataDocumentUsed = used; + + return this; + } + + + /** + * Get the client identifier used in the backchannel authentication + * request. + * + *+ * Since version 3.0.28, Authlete recognizes the {@code challenge} claim in + * Client Attestation PoP JWTs. If the {@code challenge} claim is required + * (= if the service's challenge endpoint is configured), the Authlete API + * checks whether the {@code challenge} claim in the presented Client + * Attestation PoP JWT is identical to the expected value. + *
+ * + *+ * If the {@code attestationChallenge} response parameter from the API is not + * null, its value is the expected attestation challenge value for Client + * Attestation PoP JWT. The expected value needs to be conveyed to the client + * application as the value of the {@code OAuth-Client-Attestation-Challenge} + * HTTP header. + *
+ * + *OAuth-Client-Attestation-Challenge: (The value returned from {@link #getAttestationChallenge()})
+ *
+ * + * See OAuth 2.0 Attestation-Based Client Authentication for details. + *
+ * * @since 2.32 */ public class BackchannelAuthenticationResponse extends ApiResponse { - private static final long serialVersionUID = 11L; + private static final long serialVersionUID = 13L; /** @@ -682,6 +710,22 @@ public enum Action */ private boolean clientEntityIdUsed; + /** + * The location of the client's metadata document. + * + * @since 4.29 + * @since Authlete 3.0.22 + */ + private URI metadataDocumentLocation; + + /** + * Whether the client's metadata document was used. + * + * @since 4.29 + * @since Authlete 3.0.22 + */ + private boolean metadataDocumentUsed; + /** * @since Authlete 2.0.0 */ @@ -812,6 +856,16 @@ public enum Action */ private String ticket; + /** + * The expected attestation challenge value for Client Attestation PoP JWT, + * which should be used as the value of the + * {@code OAuth-Client-Attestation-Challenge} HTTP header. + * + * @since 4.39 + * @since Authlete 3.0.28 + */ + private String attestationChallenge; + /** * Get the next action that the implementation of the backchannel @@ -1087,18 +1141,129 @@ public BackchannelAuthenticationResponse setClientEntityIdUsed(boolean used) /** - * Get the client identifier used in the backchannel authentication - * request. + * Get the location of the client's metadata document. This property + * holds a non-null value only when {@link #isMetadataDocumentUsed()} + * returns {@code true}. + * + * @return + * The location of the client's metadata document. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public URI getMetadataDocumentLocation() + { + return metadataDocumentLocation; + } + + + /** + * Set the location of the client's metadata document. This property + * should hold a non-null value only when {@link #isMetadataDocumentUsed()} + * returns {@code true}. + * + * @param location + * The location of the client's metadata document. + * + * @return + * {@code this} object. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public BackchannelAuthenticationResponse setMetadataDocumentLocation(URI location) + { + this.metadataDocumentLocation = location; + + return this; + } + + + /** + * Get the flag which indicates whether the location of the client's + * metadata document was used as a client ID. + * + *+ * This can happen when the service supports OAuth Client ID Metadata Document. + * (cf. {@link Service#isClientIdMetadataDocumentSupported()}) + *
+ * + * @return + * {@code true} if the location of client's metadata document + * was used as a client ID. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public boolean isMetadataDocumentUsed() + { + return metadataDocumentUsed; + } + + + /** + * Set the flag which indicates whether the location of the client's + * metadata document was used as a client ID. * *- * When {@link #isClientIdAliasUsed()} returns {@code true}, this method - * returns the same value as {@link #getClientIdAlias()} does. Otherwise, - * if {@link #isClientEntityIdUsed()} returns {@code true}, this method - * returns the same value as {@link #getClientEntityId()}{@code .toString()} - * does. In other cases, this method returns the string representation of - * the value returned from {@link #getClientId()}. + * This can happen when the service supports OAuth Client ID Metadata Document. + * (cf. {@link Service#isClientIdMetadataDocumentSupported()}) *
* + * @param used + * {@code true} to indicate that the location of the client's + * metadata document was used as a client ID. + * + * @return + * {@code this} object. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public BackchannelAuthenticationResponse setMetadataDocumentUsed(boolean used) + { + this.metadataDocumentUsed = used; + + return this; + } + + + /** + * Get the client identifier used in the backchannel authentication + * request. + * + *+ * When this response parameter is not null, the implementation of the + * backchannel authentication endpoint should add the + * {@code OAuth-Client-Attestation-Challenge} HTTP header in the response + * from the endpoint to the client application, using the value of this + * response parameter as the value of the HTTP header. + *
+ * + *
+ * OAuth-Client-Attestation-Challenge: (The value of this {@code attestationChallenge} response parameter)
+ *
+ *
+ * @return
+ * The expected attestation challenge value for Client Attestation
+ * PoP JWT.
+ *
+ * @since 4.39
+ * @since Authlete 3.0.28
+ *
+ * @see
+ * OAuth 2.0 Attestation-Based Client Authentication
+ */
+ public String getAttestationChallenge()
+ {
+ return attestationChallenge;
+ }
+
+
+ /**
+ * Set the expected attestation challenge value for Client Attestation PoP
+ * JWT, which should be used as the value of the
+ * {@code OAuth-Client-Attestation-Challenge} HTTP header.
+ *
+ * + * When this response parameter is not null, the implementation of the + * backchannel authentication endpoint should add the + * {@code OAuth-Client-Attestation-Challenge} HTTP header in the response + * from the endpoint to the client application, using the value of this + * response parameter as the value of the HTTP header. + *
+ * + *
+ * OAuth-Client-Attestation-Challenge: (The value of this {@code attestationChallenge} response parameter)
+ *
+ *
+ * @param challenge
+ * The expected attestation challenge value for Client Attestation
+ * PoP JWT.
+ *
+ * @return
+ * {@code this} object.
+ *
+ * @since 4.39
+ * @since Authlete 3.0.28
+ *
+ * @see
+ * OAuth 2.0 Attestation-Based Client Authentication
+ */
+ public BackchannelAuthenticationResponse setAttestationChallenge(String challenge)
+ {
+ this.attestationChallenge = challenge;
+
+ return this;
+ }
}
diff --git a/src/main/java/com/authlete/common/dto/BackchannelLogoutTokenRequest.java b/src/main/java/com/authlete/common/dto/BackchannelLogoutTokenRequest.java
new file mode 100644
index 0000000000..13f9caa876
--- /dev/null
+++ b/src/main/java/com/authlete/common/dto/BackchannelLogoutTokenRequest.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2026 Authlete, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.authlete.common.dto;
+
+
+import java.io.Serializable;
+
+
+/**
+ * Request to Authlete's
+ * /api/{serivce-id}/backchannel/logout/token
+ * API.
+ *
+ * + * The Authlete API generates a Logout Token that complies with the OpenID Connect + * Back-Channel Logout 1.0 specification. + *
+ * + * @see + * OpenID Connect Back-Channel Logout 1.0 + * + * @since 4.43 + * @since Authlete 3.0.32 + */ +public class BackchannelLogoutTokenRequest implements Serializable +{ + private static final long serialVersionUID = 1L; + + + /** + * The client identifier. + */ + private String clientIdentifier; + + + /** + * The subject of the end-user. + */ + private String subject; + + + /** + * The session ID. + */ + private String sessionId; + + + /** + * Get the client identifier. + * This request parameter is mandatory. + * + * @return + * The client identifier. + */ + public String getClientIdentifier() + { + return clientIdentifier; + } + + + /** + * Set the client identifier. + * This request parameter is mandatory. + * + * @param identifier + * The client identifier. + * + * @return + * {@code this} object. + */ + public BackchannelLogoutTokenRequest setClientIdentifier(String identifier) + { + this.clientIdentifier = identifier; + + return this; + } + + + /** + * Get the subject of the end-user. At least one of this {@code subject} + * request parameter or the {@code sessionId} request parameter is + * required. + * + *+ * This value is used as the value of the {@code sub} claim in the + * Logout Token. + *
+ * + * @return + * The subject of the end-user. + */ + public String getSubject() + { + return subject; + } + + + /** + * Set the subject of the end-user. At least one of this {@code subject} + * request parameter or the {@code sessionId} request parameter is + * required. + * + *+ * This value is used as the value of the {@code sub} claim in the + * Logout Token. + *
+ * + * @param subject + * The subject of the end-user. + * + * @return + * {@code this} object. + */ + public BackchannelLogoutTokenRequest setSubject(String subject) + { + this.subject = subject; + + return this; + } + + + /** + * Get the session ID. At least one of this {@code sessionId} request + * parameter or the {@code subject} request parameter is required. + * + *+ * This value is used as the value of the {@code sid} claim in the + * Logout Token. + *
+ * + *
+ * If the backchannel_
+ * server metadata parameter of your server is {@code true} (= if the
+ * backchannel property
+ * of your {@link Service} is {@code true}), this request parameter should
+ * be included.
+ *
+ * This value is used as the value of the {@code sid} claim in the + * Logout Token. + *
+ * + *
+ * If the backchannel_
+ * server metadata parameter of your server is {@code true} (= if the
+ * backchannel property
+ * of your {@link Service} is {@code true}), this request parameter should
+ * be included.
+ *
/api/{serivce-id}/backchannel/logout/token
+ * API.
+ *
+ * + * The Authlete API generates a Logout Token that complies with the OpenID Connect + * Back-Channel Logout 1.0 specification. + *
+ * + *+ * If the value of the {@code action} parameter in the response from the + * Authlete API is {@link Action#OK OK}, it indicates that a Logout Token has + * been generated successfully. In this case, your server is expected to send + * a Back-Channel Logout Request containing the generated Logout Token to + * the client's Backchannel Logout URI. + *
+ * + *
+ * The {@code logoutToken} parameter in the API response contains the generated
+ * Logout Token, and the backchannel parameter
+ * contains the client's Back-Channel Logout URI, which corresponds to the
+ * backchannel_ client metadata parameter.
+ * Using these values, the Back-Channel Logout Request can be constructed as
+ * follows.
+ *
+ * POST {@link #getBackchannelLogoutUri()} HTTP/1.1
+ * Content-Type: application/x-www-form-urlencoded
+ * Content-Length: ...
+ *
+ * logout_token={@link #getLogoutToken()}
+ *
+ * Note that the Authlete API does not verify whether the client's
+ * backchannel_ metadata parameter is
+ * configured properly. Therefore, the value of the
+ * backchannel response parameter may not be
+ * a valid HTTP-accessible URL. It may even be {@code null}.
+ *
+ * Also note that the API call does not revoke any refresh tokens. If necessary, + * the client itself should trigger refresh token revocation (e.g., by calling + * the revocation endpoint). + *
+ * + * @see + * OpenID Connect Back-Channel Logout 1.0 + * + * @since 4.43 + * @since Authlete 3.0.32 + */ +public class BackchannelLogoutTokenResponse extends ApiResponse +{ + private static final long serialVersionUID = 1L; + + + /** + * The next action that the API caller should take. + */ + public enum Action + { + /** + * A Logout Token has been generated successfully. The API caller is + * expected to send a Back-Channel Logout Request to the client. + */ + OK, + + + /** + * There is an error on Authlete's side, such as a database access error. + */ + SERVER_ERROR, + + + /** + * The API call is invalid. For example, the mandatory request parameter + * {@code clientIdentifier} is missing. + */ + CALLER_ERROR, + } + + + /** + * The next action that the API caller should take. + */ + private Action action; + + + /** + * The generated Logout Token. + */ + private String logoutToken; + + + /** + * The client's Back-Channel Logout URI. + */ + private URI backchannelLogoutUri; + + + /** + * Get the next action that the API caller should take. In practice, this + * value represents the result of the API call. + * + * @return + * The next action that the API caller should take. + */ + public Action getAction() + { + return action; + } + + + /** + * Set the next action that API caller should take. In practice, this + * value represents the result of the API call. + * + * @param action + * The next action that the API caller should take. + * + * @return + * {@code this} object. + */ + public BackchannelLogoutTokenResponse setAction(Action action) + { + this.action = action; + + return this; + } + + + /** + * Get the issued Logout Token. + * + * @return + * The issued Logout Token. + */ + public String getLogoutToken() + { + return logoutToken; + } + + + /** + * Set the issued Logout Token. + * + * @param logoutToken + * The issued Logout Token. + * + * @return + * {@code this} object. + */ + public BackchannelLogoutTokenResponse setLogoutToken(String logoutToken) + { + this.logoutToken = logoutToken; + + return this; + } + + + /** + * Get the client's Back-Channel Logout URI, which corresponds to the + *backchannel_logout_uri client metadata
+ * parameter defined in the OpenID
+ * Connect Back-Channel Logout 1.0 specification.
+ *
+ * @return
+ * The client's Back-Channel Logout URI.
+ */
+ public URI getBackchannelLogoutUri()
+ {
+ return backchannelLogoutUri;
+ }
+
+
+ /**
+ * Set the client's Back-Channel Logout URI, which corresponds to the
+ * backchannel_logout_uri client metadata
+ * parameter defined in the OpenID
+ * Connect Back-Channel Logout 1.0 specification.
+ *
+ * @param uri
+ * The client's Back-Channel Logout URI.
+ *
+ * @return
+ * {@code this} object.
+ */
+ public BackchannelLogoutTokenResponse setBackchannelLogoutUri(URI uri)
+ {
+ this.backchannelLogoutUri = uri;
+
+ return this;
+ }
+}
diff --git a/src/main/java/com/authlete/common/dto/CimdOptions.java b/src/main/java/com/authlete/common/dto/CimdOptions.java
new file mode 100644
index 0000000000..a02389ed3e
--- /dev/null
+++ b/src/main/java/com/authlete/common/dto/CimdOptions.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2025 Authlete, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.authlete.common.dto;
+
+
+import java.io.Serializable;
+
+
+/**
+ * Options for OAuth Client ID Metadata Document (CIMD).
+ *
+ * @since 4.30
+ * @since Authlete 3.0.22
+ *
+ * @see
+ * OAuth Client ID Metadata Document
+ */
+public class CimdOptions implements Serializable
+{
+ private static final long serialVersionUID = 1L;
+
+
+ /**
+ * Whether to always retrieve client metadata in the CIMD context
+ * regardless of the cache's validity.
+ */
+ private boolean alwaysRetrieved;
+
+
+ /**
+ * Whether to allow the {@code http} scheme in client IDs in the CIMD
+ * context.
+ */
+ private boolean httpPermitted;
+
+
+ /**
+ * Whether to allow a query component in client IDs in the CIMD context.
+ */
+ private boolean queryPermitted;
+
+
+ /**
+ * Get the flag that indicates whether Authlete should always fetch the
+ * client metadata from the location specified by the client ID, regardless
+ * of whether a cached copy already exists and has not yet expired.
+ *
+ * + * Under normal circumstances, client metadata retrieved from the location + * referenced by the client ID is stored in the database with an expiration + * time calculated using HTTP caching mechanisms (see RFC 9111 HTTP Caching). + * Until that expiration time is reached, Authlete does not attempt to + * retrieve the client metadata again. + *
+ * + *+ * When this flag is set to {@code true}, Authlete retrieves the client + * metadata regardless of the cache's validity. + *
+ * + *+ * If this flag is included in an Authlete API call and its value is + * {@code true}, it takes precedence over the corresponding service + * configuration (see {@link Service#isCimdAlwaysRetrieved()}). + *
+ * + *+ * This flag is effective only when the service supports CIMD (see {@link + * Service#isClientIdMetadataDocumentSupported()}) and CIMD is actually + * used to resolve client metadata. For example, if the client ID in a + * request does not appear to be a valid URI, CIMD will not be used even + * if the service is configured to support it. In such cases, this flag + * has no effect. + *
+ * + *+ * Client metadata retrieval is performed only in the initiating request + * of an authorization flow, and not in any subsequent requests. For + * example, in the authorization code flow, metadata may be retrieved + * during the authorization request, but not during the subsequent token + * request. In contrast, in the client credentials flow, metadata retrieval + * may occur because the token request itself is the initiating request + * in the flow. + *
+ * + * @return + * {@code true} if Authlete attempts to retrieve client metadata + * regardless of the cache's validity. + */ + public boolean isAlwaysRetrieved() + { + return alwaysRetrieved; + } + + + /** + * Set the flag that indicates whether Authlete should always fetch the + * client metadata from the location specified by the client ID, regardless + * of whether a cached copy already exists and has not yet expired. + * + *+ * Under normal circumstances, client metadata retrieved from the location + * referenced by the client ID is stored in the database with an expiration + * time calculated using HTTP caching mechanisms (see RFC 9111 HTTP Caching). + * Until that expiration time is reached, Authlete does not attempt to + * retrieve the client metadata again. + *
+ * + *+ * When this flag is set to {@code true}, Authlete retrieves the client + * metadata regardless of the cache's validity. + *
+ * + *+ * If this flag is included in an Authlete API call and its value is + * {@code true}, it takes precedence over the corresponding service + * configuration (see {@link Service#isCimdAlwaysRetrieved()}). + *
+ * + *+ * This flag is effective only when the service supports CIMD (see {@link + * Service#isClientIdMetadataDocumentSupported()}) and CIMD is actually + * used to resolve client metadata. For example, if the client ID in a + * request does not appear to be a valid URI, CIMD will not be used even + * if the service is configured to support it. In such cases, this flag + * has no effect. + *
+ * + *+ * Client metadata retrieval is performed only in the initiating request + * of an authorization flow, and not in any subsequent requests. For + * example, in the authorization code flow, metadata may be retrieved + * during the authorization request, but not during the subsequent token + * request. In contrast, in the client credentials flow, metadata retrieval + * may occur because the token request itself is the initiating request + * in the flow. + *
+ * + * @param always + * {@code true} to instruct Authlete to retrieve client metadata + * regardless of the cache's validity. + * + * @return + * {@code this} object. + */ + public CimdOptions setAlwaysRetrieved(boolean always) + { + this.alwaysRetrieved = always; + + return this; + } + + + /** + * Get the flag that indicates whether the {@code http} scheme in the client + * ID is permitted. + * + *+ * The specification requires the {@code https} scheme, but if this flag is + * set to {@code true}, Authlete also allows the {@code http} scheme. The + * main purpose of this option is to make development easier for developers + * who run CIMD-enabled servers and a web server publishing client metadata + * on their local machines without TLS. + *
+ * + *+ * Given this purpose, it is not recommended to enable this option in + * production environments unless an allowlist is used (see {@link + * Service#isCimdAllowlistEnabled()}). + *
+ * + *+ * If this flag is included in an Authlete API call and its value is + * {@code true}, it takes precedence over the corresponding service + * configuration (see {@link Service#isCimdHttpPermitted()}). + *
+ * + * @return + * {@code true} if the {@code http} scheme in the client ID is + * permitted. + */ + public boolean isHttpPermitted() + { + return httpPermitted; + } + + + /** + * Set the flag that indicates whether the {@code http} scheme in the client + * ID is permitted. + * + *+ * The specification requires the {@code https} scheme, but if this flag is + * set to {@code true}, Authlete also allows the {@code http} scheme. The + * main purpose of this option is to make development easier for developers + * who run CIMD-enabled servers and a web server publishing client metadata + * on their local machines without TLS. + *
+ * + *+ * Given this purpose, it is not recommended to enable this option in + * production environments unless an allowlist is used (see {@link + * Service#isCimdAllowlistEnabled()}). + *
+ * + *+ * If this flag is included in an Authlete API call and its value is + * {@code true}, it takes precedence over the corresponding service + * configuration (see {@link Service#isCimdHttpPermitted()}). + *
+ * + * @param permitted + * {@code true} to permit the {@code http} scheme in the client ID. + * + * @return + * {@code this} object. + */ + public CimdOptions setHttpPermitted(boolean permitted) + { + this.httpPermitted = permitted; + + return this; + } + + + /** + * Get the flag that indicates whether a query component in the client ID + * is permitted. + * + *+ * Although the specification states that a client ID "SHOULD NOT + * include a query string component," it does technically allow it. + * However, query components are prone to misuse. Therefore, Authlete does + * not allow them by default. Setting this flag to {@code true} relaxes + * that restriction. + *
+ * + *+ * If this flag is included in an Authlete API call and its value is + * {@code true}, it takes precedence over the corresponding service + * configuration (see {@link Service#isCimdQueryPermitted()}). + *
+ * + * @return + * {@code true} if a query component in the client ID is permitted. + */ + public boolean isQueryPermitted() + { + return queryPermitted; + } + + + /** + * Set the flag that indicates whether a query component in the client ID + * is permitted. + * + *+ * Although the specification states that a client ID "SHOULD NOT + * include a query string component," it does technically allow it. + * However, query components are prone to misuse. Therefore, Authlete does + * not allow them by default. Setting this flag to {@code true} relaxes + * that restriction. + *
+ * + *+ * If this flag is included in an Authlete API call and its value is + * {@code true}, it takes precedence over the corresponding service + * configuration (see {@link Service#isCimdQueryPermitted()}). + *
+ * + * @param permitted + * {@code true} to permit a query component in the client ID. + * + * @return + * {@code this} object. + */ + public CimdOptions setQueryPermitted(boolean permitted) + { + this.queryPermitted = permitted; + + return this; + } +} diff --git a/src/main/java/com/authlete/common/dto/Client.java b/src/main/java/com/authlete/common/dto/Client.java index 63ca8992f7..7209308ea8 100644 --- a/src/main/java/com/authlete/common/dto/Client.java +++ b/src/main/java/com/authlete/common/dto/Client.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2024 Authlete, Inc. + * Copyright (C) 2014-2026 Authlete, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ import com.authlete.common.types.ApplicationType; import com.authlete.common.types.ClientAuthMethod; import com.authlete.common.types.ClientRegistrationType; +import com.authlete.common.types.ClientSource; import com.authlete.common.types.ClientType; import com.authlete.common.types.DeliveryMode; import com.authlete.common.types.FapiMode; @@ -80,10 +81,16 @@ * * @see OpenID for Verifiable Credential Issuance + * + * @see OAuth Client ID Metadata Document + * + * @see OpenID Connect Back-Channel Logout 1.0 */ public class Client implements Serializable { - private static final long serialVersionUID = 38L; + private static final long serialVersionUID = 42L; /* @@ -583,6 +590,13 @@ public class Client implements Serializable */ private boolean mtlsEndpointAliasesUsed; + /** + * Indicates if the client is in scope for token migration. + * + * @since Authlete 4.23 + */ + private boolean inScopeForTokenMigration; + /* * For OpenID Federation 1.0. @@ -638,6 +652,7 @@ public class Client implements Serializable */ private boolean explicitlyRegistered; + /* * For Verifiable Credentials */ @@ -653,6 +668,98 @@ public class Client implements Serializable private boolean credentialResponseEncryptionRequired; + /* + * For OAuth Client ID Metadata Document + */ + + /** + * The location of the client's metadata document. + * + * @since 4.29 + * @since Authlete 3.0.22 + */ + private URI metadataDocumentLocation; + + /** + * The time at which the metadata document will expire. + * + * @since 4.29 + * @since Authlete 3.0.22 + */ + private long metadataDocumentExpiresAt; + + /** + * The time at which the metadata document was updated. + * + * @since 4.29 + * @since Authlete 3.0.22 + */ + private long metadataDocumentUpdatedAt; + + /** + * Whether the client was discovered by Client ID Metadata Document. + * + * @since 4.29 + * @since Authlete 3.0.22 + */ + private boolean discoveredByMetadataDocument; + + + /** + * The source of this client. + * + * @since 4.29 + * @since Authlete 3.0.22 + */ + private ClientSource clientSource; + + + /** + * The SPIFFE ID. + * + * @since 4.36 + * @since Authlete 3.0.28 + */ + private URI spiffeId; + + + /** + * The SPIFFE Bundle Endpoint. + * + * @since 4.36 + * @since Authlete 3.0.28 + */ + private URI spiffeBundleEndpoint; + + + /** + * The HAIP version to comply with. + * + * @since 4.41 + * @since Authlete 3.0.29 + */ + private String haipVersion; + + + /** + * The Back-Channel Logout URI. + * + * @since 4.44 + * @since Authlete 3.0.32 + */ + private URI backchannelLogoutUri; + + + /** + * The flag indicating whether this client requires Logout Tokens to + * include the {@code sid} claim. + * + * @since 4.44 + * @since Authlete 3.0.32 + */ + private boolean backchannelLogoutSessionRequired; + + /** * Get the client number. * @@ -5209,6 +5316,710 @@ public Client setMtlsEndpointAliasesUsed(boolean mtlsEndpointAliasesUsed) } + /** + * Get the value that indicates whether this Client is inscope for token migration. + * + * @return + * The value that indicates whether this Client is in scope for token migration. + * + * @since Authlete 4.23 + */ + public boolean isInScopeForTokenMigration() + { + return inScopeForTokenMigration; + } + + + /** + * Sets the flag that indicates that this Client is in scope for token migration. + * + * @param inScopeForTokenMigration + * The new value for the flag to indicates whether this Client is in scope for token migration. + * + * @return + * The {@link Client} after setting the provided property. + * + * @since Authlete 4.23 + */ + public Client setInScopeForTokenMigration(boolean inScopeForTokenMigration) + { + this.inScopeForTokenMigration = inScopeForTokenMigration; + + return this; + } + + + /** + * Get the location of the client's metadata document. + * + *+ * This property is available only when the client was discovered using + * the mechanism defined in OAuth Client ID Metadata Document. The mechanism is used only + * when the service supports it. + * (cf. {@link Service#isClientIdMetadataDocumentSupported()}) + *
+ * + * @return + * The location of the client's metadata document. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public URI getMetadataDocumentLocation() + { + return metadataDocumentLocation; + } + + + /** + * Set the location of the client's metadata document. + * + *+ * This property is available only when the client was discovered using + * the mechanism defined in OAuth Client ID Metadata Document. The mechanism is used only + * when the service supports it. + * (cf. {@link Service#isClientIdMetadataDocumentSupported()}) + *
+ * + * @param location + * The location of the client's metadata document. + * + * @return + * {@code this} object. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public Client setMetadataDocumentLocation(URI location) + { + this.metadataDocumentLocation = location; + + return this; + } + + + /** + * Get the expiration time of the client's metadata document. The value is + * represented as milliseconds elapsed since the Unix epoch (1970-01-01). + * + *+ * This property is meaningful only when the client was discovered using + * the mechanism defined in OAuth Client ID Metadata Document. The mechanism is used only + * when the service supports it. + * (cf. {@link Service#isClientIdMetadataDocumentSupported()}) + *
+ * + * @return + * The expiration time of the client's metadata document. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public long getMetadataDocumentExpiresAt() + { + return metadataDocumentExpiresAt; + } + + + /** + * Set the expiration time of the client's metadata document. The value is + * represented as milliseconds elapsed since the Unix epoch (1970-01-01). + * + *+ * This property is meaningful only when the client was discovered using + * the mechanism defined in OAuth Client ID Metadata Document. The mechanism is used only + * when the service supports it. + * (cf. {@link Service#isClientIdMetadataDocumentSupported()}) + *
+ * + * @param expiresAt + * The expiration time of the client's metadata document. + * + * @return + * {@code this} object. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public Client setMetadataDocumentExpiresAt(long expiresAt) + { + this.metadataDocumentExpiresAt = expiresAt; + + return this; + } + + + /** + * Get the time at which the client was last updated with its metadata + * document. + * + *+ * This property is meaningful only when the client was discovered using + * the mechanism defined in OAuth Client ID Metadata Document. The mechanism is used only + * when the service supports it. + * (cf. {@link Service#isClientIdMetadataDocumentSupported()}) + *
+ * + * @return + * The time at which the client was last updated with its metadata + * document. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public long getMetadataDocumentUpdatedAt() + { + return metadataDocumentUpdatedAt; + } + + + /** + * Set the time at which the client was last updated with its metadata + * document. + * + *+ * This property is meaningful only when the client was discovered using + * the mechanism defined in OAuth Client ID Metadata Document. The mechanism is used only + * when the service supports it. + * (cf. {@link Service#isClientIdMetadataDocumentSupported()}) + *
+ * + * @param updatedAt + * The time at which the client was last updated with its metadata + * document. + * + * @return + * {@code this} object. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public Client setMetadataDocumentUpdatedAt(long updatedAt) + { + this.metadataDocumentUpdatedAt = updatedAt; + + return this; + } + + + /** + * Get the flag indicating whether this client was discovered and + * registered by the mechanism defined in OAuth Client ID Metadata Document. + * + * @return + * {@code true} if this client was discovered and registered by + * the mechanism defined in OAuth Client ID Metadata Document. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public boolean isDiscoveredByMetadataDocument() + { + return discoveredByMetadataDocument; + } + + + /** + * Set the flag indicating whether this client was discovered and + * registered by the mechanism defined in OAuth Client ID Metadata Document. + * + * @param discovered + * {@code true} to indicate that this client was discovered and + * registered by the mechanism defined in OAuth Client ID Metadata + * Document. + * + * @return + * {@code this} object. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public Client setDiscoveredByMetadataDocument(boolean discovered) + { + this.discoveredByMetadataDocument = discovered; + + return this; + } + + + /** + * Get the source of this client. + * + * @return + * The source of this client. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see #updateClientSource() + */ + public ClientSource getClientSource() + { + return clientSource; + } + + + /** + * Set the source of this client. + * + * @param source + * The source of this client. + * + * @return + * {@code this} object. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see #updateClientSource() + */ + public Client setClientSource(ClientSource source) + { + this.clientSource = source; + + return this; + } + + + /** + * Update the {@code clientSource} property according to the current + * values of registration-related fields. + * + *+ * The value of this property is compared against the SPIFFE ID contained + * in the SVID presented by the client during SPIFFE Client Authentication. + *
+ * + *
+ * According to the SPIFFE
+ * ID specification, the characters allowed in the path component of a
+ * SPIFFE ID are limited to {@code [a-zA-Z0-9.-_]}. However, in the value
+ * of this property (i.e., in the value of the {@code spiffe_id} client
+ * metadata), the final path segment may be specified using a wildcard
+ * {@code *} (e.g., spiffe://).
+ * When comparing it with the SPIFFE ID in the SVID, the wildcard is taken
+ * into account.
+ *
+ * The value of this property is compared against the SPIFFE ID contained + * in the SVID presented by the client during SPIFFE Client Authentication. + *
+ * + *
+ * According to the SPIFFE
+ * ID specification, the characters allowed in the path component of a
+ * SPIFFE ID are limited to {@code [a-zA-Z0-9.-_]}. However, in the value
+ * of this property (i.e., in the value of the {@code spiffe_id} client
+ * metadata), the final path segment may be specified using a wildcard
+ * {@code *} (e.g., spiffe://).
+ * When comparing it with the SPIFFE ID in the SVID, the wildcard is taken
+ * into account.
+ *
+ * The authorization server retrieves the SPIFFE Bundle from the location + * indicated by this property (i.e., by the {@code spiffe_bundle_endpoint} + * client metadata) in order to verify the SVID presented by the client + * during SPIFFE Client Authentication. The SPIFFE Bundle is, in essence, + * a JWK Set. + *
+ * + * @return + * The SPIFFE Bundle Endpoint. + * + * @since 4.36 + * @since Authlete 3.0.28 + * + * @see SPIFFE + * @see + * OAuth SPIFFE Client Authentication + */ + public URI getSpiffeBundleEndpoint() + { + return spiffeBundleEndpoint; + } + + + /** + * Set the SPIFFE Bundle Endpoint. This property corresponds to the + * {@code spiffe_bundle_endpoint} client metadata. + * + *+ * The authorization server retrieves the SPIFFE Bundle from the location + * indicated by this property (i.e., by the {@code spiffe_bundle_endpoint} + * client metadata) in order to verify the SVID presented by the client + * during SPIFFE Client Authentication. The SPIFFE Bundle is, in essence, + * a JWK Set. + *
+ * + * @param endpoint + * The SPIFFE Bundle Endpoint. + * + * @since 4.36 + * @since Authlete 3.0.28 + * + * @see SPIFFE + * @see + * OAuth SPIFFE Client Authentication + */ + public Client setSpiffeBundleEndpoint(URI endpoint) + { + this.spiffeBundleEndpoint = endpoint; + + return this; + } + + + /** + * Get the HAIP version to comply with. Valid values for this property are + * as follows: + * + *+ * If this property is set, requests from this client undergo additional + * validation based on the specified version of the HAIP specification. + *
+ * + *+ * Note that even if this property is not set, additional validation based + * on the HAIP specification is performed if the {@link Service}'s + * {@code haipVersion} is set or if a scope with the {@code haip} attribute + * is included in the request. + *
+ * + * @return + * The HAIP version to comply with. + * + * @since 4.41 + * @since Authlete 3.0.29 + * + * @see + * OpenID4VC High Assurance Interoperability Profile 1.0 + */ + public String getHaipVersion() + { + return haipVersion; + } + + + /** + * Set the HAIP version to comply with. Valid values for this property are + * as follows: + * + *+ * If this property is set, requests from this client undergo additional + * validation based on the specified version of the HAIP specification. + *
+ * + *+ * Note that even if this property is not set, additional validation based + * on the HAIP specification is performed if the {@link Service}'s + * {@code haipVersion} is set or if a scope with the {@code haip} attribute + * is included in the request. + *
+ * + * @param version + * The HAIP version to comply with. + * + * @return + * {@code this} object. + * + * @since 4.41 + * @since Authlete 3.0.29 + * + * @see + * OpenID4VC High Assurance Interoperability Profile 1.0 + */ + public Client setHaipVersion(String version) + { + this.haipVersion = version; + + return this; + } + + + /** + * Get the Back-Channel Logout URI. This property corresponds to the + *backchannel_logout_uri client metadata parameter
+ * defined in the OpenID
+ * Connect Back-Channel Logout 1.0 specification.
+ *
+ * @return
+ * The Back-Channel Logout URI.
+ *
+ * @since 4.44
+ * @since Authlete 3.0.32
+ *
+ * @see
+ * OpenID Connect Back-Channel Logout 1.0
+ */
+ public URI getBackchannelLogoutUri()
+ {
+ return backchannelLogoutUri;
+ }
+
+
+ /**
+ * Set the Back-Channel Logout URI. This property corresponds to the
+ * backchannel_logout_uri client metadata parameter
+ * defined in the OpenID
+ * Connect Back-Channel Logout 1.0 specification.
+ *
+ * @param uri
+ * The Back-Channel Logout URI.
+ *
+ * @return
+ * {@code this} object.
+ *
+ * @since 4.44
+ * @since Authlete 3.0.32
+ *
+ * @see
+ * OpenID Connect Back-Channel Logout 1.0
+ */
+ public Client setBackchannelLogoutUri(URI uri)
+ {
+ this.backchannelLogoutUri = uri;
+
+ return this;
+ }
+
+
+ /**
+ * Get the flag indicating whether this client requires Logout Tokens to include the {@code sid} claim. This property
+ * corresponds to the backchannel_logout_sesssion_required
+ * client metadata parameter defined in the OpenID
+ * Connect Back-Channel Logout 1.0 specification.
+ *
+ * @return
+ * The flag indicating whether this client requires Logout Tokens
+ * to include the {@code sid} claim.
+ *
+ * @since 4.44
+ * @since Authlete 3.0.32
+ *
+ * @see
+ * OpenID Connect Back-Channel Logout 1.0
+ */
+ public boolean isBackchannelLogoutSessionRequired()
+ {
+ return backchannelLogoutSessionRequired;
+ }
+
+
+ /**
+ * Set the flag indicating whether this client requires Logout Tokens to include the {@code sid} claim. This property
+ * corresponds to the backchannel_logout_sesssion_required
+ * client metadata parameter defined in the OpenID
+ * Connect Back-Channel Logout 1.0 specification.
+ *
+ * @param required
+ * {@code true} to require Logout Tokens to include the {@code sid}
+ * claim.
+ *
+ * @return
+ * {@code this} object.
+ *
+ * @since 4.44
+ * @since Authlete 3.0.32
+ *
+ * @see
+ * OpenID Connect Back-Channel Logout 1.0
+ */
+ public Client setBackchannelLogoutSessionRequired(boolean required)
+ {
+ this.backchannelLogoutSessionRequired = required;
+
+ return this;
+ }
+
+
/**
* Get a {@code Map} instance that represents a set of standard client
* metadata.
@@ -5502,6 +6313,26 @@ public Map@@ -41,14 +41,59 @@ * * *
- * The role of the {@code /vci/deferred/issue} API is to issue a credential. + * The {@code /vci/deferred/issue} API is used for one of the following purposes: *
* + *- * If the credential for the transaction ID is not ready, the implementation - * of the deferred credential endpoint should prepare an error response with - * {@code "error":"issuance_pending"} manually and return it to the request - * sender, without calling the {@code /vci/deferred/issue} API. + * To issue a credential, the {@code order} request parameter must be set up + * properly, and both the {@code denied} and {@code order.issuanceDeferred} + * request parameters must be set to {@code false}. + *
+ * + *
+ * To deny the deferred credential request, the {@code denied} request parameter
+ * in the API call must be set to {@code true}. In this case, Authlete prepares
+ * a response containing {@code "error":"credential_request_denied"} and returns
+ * "action":"{@link CredentialDeferredIssueResponse.Action#BAD_REQUEST BAD_REQUEST}".
+ *
+ * Note that the {@code credential_request_denied} error code does not exist in + * OID4VCI 1.0 ID1. Therefore, you should not call the API with {@code denied} + * set to {@code true} when the target specification version is "1.0-ID1". + *
+ * + *+ * To notify that the requested credential is not ready yet, the + * {@code order.issuanceDeferred} request parameter in the API call must be + * set to {@code true}. In this case, Authlete will do one of the following: + *
+ * + *+ * Note that if the Authlete Server version is older than 3.0.25 and the target + * specification version is "1.0-ID1", you need to manually construct an error + * response as shown below, without using the {@code /vci/deferred/issue} API. *
* *@@ -61,15 +106,38 @@ * } ** + *
+ * If the {@code interval} request parameter in the API call is present and its + * value is positive, it is used as the value of the {@code interval} response + * parameter in the deferred credential response. If the {@code interval} + * request parameter is missing, zero, or negative, Authlete uses the default + * value. + *
+ * + *+ * Note that the {@code interval} response parameter does not exist in OID4VCI + * 1.0 ID1. Therefore, you don't have to care about the {@code interval} request + * parameter if the target specification version is "1.0-ID1". + *
+ * + *+ * In all cases, the {@code order.requestIdentifier} request parameter must be + * set properly. + *
+ * * @since 3.70 * @since Authlete 3.0 * - * @see OpenID for Verifiable Credential Issuance + * @see + * OpenID for Verifiable Credential Issuance 1.0 */ public class CredentialDeferredIssueRequest implements Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 2L; /** @@ -78,6 +146,26 @@ public class CredentialDeferredIssueRequest implements Serializable private CredentialIssuanceOrder order; + /** + * The flag that indicates whether to deny the deferred credential request. + * + * @since 4.35 + * @since Authlete 3.0.25 + */ + private boolean denied; + + + /** + * The minimum amount of time in seconds that the Wallet SHOULD wait after + * receiving the response before sending a new request to the Deferred + * Credential Endpoint. + * + * @since 4.35 + * @since Authlete 3.0.25 + */ + private int interval; + + /** * Get the credential order that provides an instruction for issuing a * credential. @@ -107,4 +195,160 @@ public CredentialDeferredIssueRequest setOrder(CredentialIssuanceOrder order) return this; } + + + /** + * Get the flag that indicates whether to deny the deferred credential + * request. + * + *+ * If this parameter is set to {@code true}, the response content prepared + * by Authlete will contain {@code "error":"credential_request_denied"} + * like below. + *
+ * + *
+ * {
+ * "error": "credential_request_denied"
+ * }
+ *
+ *
+ * + * Note that the {@code credential_request_denied} error code does not + * exist in OID4VCI 1.0 ID1. + *
+ * + * @return + * {@code true} for generating a response content containing + * {@code "error":"credential_request_denied"}. + * + * @since 4.35 + * @since Authlete 3.0.25 + */ + public boolean isDenied() + { + return denied; + } + + + /** + * Set the flag that indicates whether to deny the deferred credential + * request. + * + *+ * If this parameter is set to {@code true}, the response content prepared + * by Authlete will contain {@code "error":"credential_request_denied"} + * like below. + *
+ * + *
+ * {
+ * "error": "credential_request_denied"
+ * }
+ *
+ *
+ * + * Note that the {@code credential_request_denied} error code does not + * exist in OID4VCI 1.0 ID1. + *
+ * + * @param denied + * {@code true} for generating a response content containing + * {@code "error":"credential_request_denied"}. + * + * @return + * {@code this} object. + * + * @since 4.35 + * @since Authlete 3.0.25 + */ + public CredentialDeferredIssueRequest setDenied(boolean denied) + { + this.denied = denied; + + return this; + } + + + /** + * Get the minimum amount of time in seconds that the Wallet SHOULD wait + * after receiving the response before sending a new request to the + * Deferred Credential Endpoint. + * + *+ * The value of this parameter is used as the value of the {@code interval} + * parameter in the deferred credential response. However, the + * {@code interval} parameter is included in the deferred credential + * response only when the following conditions are met: + *
+ * + *+ * The value of this parameter is used as the value of the {@code interval} + * parameter in the deferred credential response. However, the + * {@code interval} parameter is included in the deferred credential + * response only when the following conditions are met: + *
+ * + *+ * The {@code action} value {@link Action#ACCEPTED ACCEPTED} means that + * the requested credential is not ready yet. In this case, the + * implementation of the deferred credential endpoint should return a + * successful response to the request sender. The HTTP status code and the + * content type of the response should be 202 and {@code application/json}, + * respectively. The value of the {@code responseContent} parameter can + * be used as the message body of the response. It contains the + * "{@code transaction_id}" parameter that conforms to the specification + * of "Deferred Credential Response". + *
+ * + *+ * HTTP/1.1 202 Accepted + * Content-Type: application/json + * Cache-Control: no-store + * + * (Put the value of the "responseContent" parameter here.) + *+ * + *
+ * The {@code action} value {@link Action#ACCEPTED_JWT ACCEPTED_JWT} means + * that the requested credential is not ready yet and the deferred credential + * response should be encrypted. In this case, the implementation of the + * deferred credential endpoint should return a successful response to the + * request sender. The HTTP status code and the content type of the response + * should be 202 and {@code application/jwt}, respectively. The value of the + * {@code responseContent} parameter is an encrypted JWT and can be used + * as the message body of the response. + *
+ * + *+ * HTTP/1.1 202 Accepted + * Content-Type: application/jwt + * Cache-Control: no-store + * + * (Put the value of the "responseContent" parameter here.) + *+ * + *
@@ -169,7 +214,7 @@ */ public class CredentialDeferredIssueResponse extends ApiResponse { - private static final long serialVersionUID = 2L; + private static final long serialVersionUID = 3L; /** @@ -197,12 +242,42 @@ public enum Action */ OK_JWT, + /** + * The requested credential is not ready yet. The implementation of + * the deferred credential endpoint should return a successful response + * with the HTTP status code "202 Accepted" and the content type + * {@code application/json}. + * + * @since 4.35 + * @since Authlete 3.0.25 + */ + ACCEPTED, + + /** + * The requested credential is not ready yet and the deferred + * credential response should be encrypted. The implementation of the + * deferred credential endpoint should return a successful response + * with the HTTP status code "202 Accepted" and the content type + * {@code application/jwt}. + * + * @since 4.35 + * @since Authlete 3.0.25 + */ + ACCEPTED_JWT, + /** * The original deferred credential request is wrong. This can happen, * for example, when the process for encrypting the deferred credential * response with the encryption parameters specified in the deferred * credential request failed. * + *
+ * Since Authlete 3.0.25, this action is returned also when the + * credential issuer has decided not to issue a credential for the + * presented transaction ID (that is, when the {@code /vci/deferred/issue} + * API is called with {@code denied} set to {@code true}). + *
+ * * @since 3.86 */ BAD_REQUEST, diff --git a/src/main/java/com/authlete/common/dto/CredentialDeferredParseResponse.java b/src/main/java/com/authlete/common/dto/CredentialDeferredParseResponse.java index 778bfda188..3891482730 100644 --- a/src/main/java/com/authlete/common/dto/CredentialDeferredParseResponse.java +++ b/src/main/java/com/authlete/common/dto/CredentialDeferredParseResponse.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 Authlete, Inc. + * Copyright (C) 2023-2026 Authlete, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -33,14 +33,15 @@ * The {@code action} value {@link Action#OK OK} means that the deferred * credential request is valid. In this case, the implementation of the * deferred credential endpoint should call the {@code /vci/deferred/issue} - * API in order to issue a verifiable credential, or return the - * {@code issuance_pending} error if the verifiable credential is not ready - * yet. + * API in order to issue a verifiable credential, deny the deferred credential + * request, or notify that the requested credential is not ready yet. * * *- * The following is an example error response telling the request sender - * that the verifiable credential is not ready yet. + * Note that if the Authlete Server version is older than 3.0.25 and the + * target specification version is "1.0-ID1", you need to manually construct + * an error response containing {@code "error":"issuance_pending"}, as shown + * below, without using the {@code /vci/deferred/issue} API. *
* *diff --git a/src/main/java/com/authlete/common/dto/CredentialIssuerMetadata.java b/src/main/java/com/authlete/common/dto/CredentialIssuerMetadata.java index 0a002df1b8..7b0fb32090 100644 --- a/src/main/java/com/authlete/common/dto/CredentialIssuerMetadata.java +++ b/src/main/java/com/authlete/common/dto/CredentialIssuerMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2024 Authlete, Inc. + * Copyright (C) 2023-2025 Authlete, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,26 +18,103 @@ import static com.authlete.common.util.MapUtils.put; import static com.authlete.common.util.MapUtils.putJsonObject; +import static com.authlete.common.util.MapUtils.putJsonArray; import java.io.Serializable; import java.net.URI; import java.util.LinkedHashMap; import java.util.Map; import com.authlete.common.types.JWEAlg; import com.authlete.common.types.JWEEnc; +import com.authlete.common.types.JWEZip; +import com.nimbusds.jose.jwk.JWKSet; /** * A class that represents the set of credential issuer metadata. - * The set consists of the following. + * The set consists of the following: + * + *+ *+ * + *+ *
+ *+ * + *Parameter + *Spec Version + *+ * + *1.0-ID1 + *1.0-Final + *+ * + *{@code credential_issuer} + *✓ + *✓ + *+ * + *{@code authorization_servers} + *✓ + *✓ + *+ * + *{@code credential_endpoint} + *✓ + *✓ + *+ * + *{@code batch_credential_endpoint} + *✓ + *deprecated + *+ * + *{@code deferred_credential_endpoint} + *✓ + *✓ + *+ * + *{@code notification_endpoint} + *✓ + *✓ + *+ * + *{@code nonce_endpoint} + *+ * ✓ + *+ * + *{@code credential_request_encryption} + *+ * ✓ + *+ * + *{@code credential_response_encryption} + *✓ + *✓ + *+ * + *{@code batch_credential_issuance} + *+ * ✓ + *+ * + *{@code display} + *✓ + *✓ + *+ * + *{@code credential_configurations_supported} + *✓ + *✓ + *+ * The following parameters, which existed in the 1.0-ID1 version but have + * removed from the 1.0-Final version, are not supported by Authlete + * from the beginning: + *
* *
@@ -76,14 +153,14 @@ * @since Authlete 3.0 * * @see OpenID for Verifiable Credential Issuance + * >OpenID for Verifiable Credential Issuance 1.0 * - * @see OpenID for Verifiable Credential Issuance, Working Draft + * @see OpenID for Verifiable Credential Issuance 1.0, Implementer's Draft 1 */ public class CredentialIssuerMetadata implements Serializable { - private static final long serialVersionUID = 4L; + private static final long serialVersionUID = 5L; /** @@ -119,6 +196,22 @@ public class CredentialIssuerMetadata implements Serializable private URI deferredCredentialEndpoint; + /** + * The URL of the notification endpoint. + * + * @since 4.26 + */ + private URI notificationEndpoint; + + + /** + * The URL of the nonce endpoint. + * + * @since 4.26 + */ + private URI nonceEndpoint; + + /** * The supported JWE alg algorithms for credential response encryption. * @@ -127,6 +220,40 @@ public class CredentialIssuerMetadata implements Serializable private JWEAlg[] credentialResponseEncryptionAlgValuesSupported; + /** + * A JSON Web Key Set containing private keys for credential request + * encryption. + * + * @since 4.26 + */ + private String credentialRequestEncryptionJwks; + + + /** + * The supported JWE enc algorithms for credential request encryption. + * + * @since 4.26 + */ + private JWEEnc[] credentialRequestEncryptionEncValuesSupported; + + + /** + * The supported JWE zip algorithms for credential request encryption. + * + * @since 4.26 + */ + private JWEZip[] credentialRequestEncryptionZipValuesSupported; + + + /** + * The boolean flag indicating whether credential request encryption is + * required. + * + * @since 4.26 + */ + private boolean requireCredentialRequestEncryption; + + /** * The supported JWE enc algorithms for credential response encryption. * @@ -135,6 +262,14 @@ public class CredentialIssuerMetadata implements Serializable private JWEEnc[] credentialResponseEncryptionEncValuesSupported; + /** + * The supported JWE zip algorithms for credential response encryption. + * + * @since 4.26 + */ + private JWEZip[] credentialResponseEncryptionZipValuesSupported; + + /** * The boolean flag indicating whether credential response encryption is * required. @@ -144,6 +279,23 @@ public class CredentialIssuerMetadata implements Serializable private boolean requireCredentialResponseEncryption; + /** + * The maximum array size for the {@code proofs} parameter in a credential + * request. + * + * @since 4.26 + */ + private int batchSize; + + + /** + * The credential issuer's display properties in JSON array format. + * + * @since 4.26 + */ + private String display; + + /** * A JSON object describing supported credential configurations. */ @@ -176,9 +328,18 @@ public CredentialIssuerMetadata(CredentialIssuerMetadata metadata) credentialEndpoint = metadata.getCredentialEndpoint(); batchCredentialEndpoint = metadata.getBatchCredentialEndpoint(); deferredCredentialEndpoint = metadata.getDeferredCredentialEndpoint(); + notificationEndpoint = metadata.getNotificationEndpoint(); + nonceEndpoint = metadata.getNonceEndpoint(); + credentialRequestEncryptionJwks = metadata.getCredentialRequestEncryptionJwks(); + credentialRequestEncryptionEncValuesSupported = metadata.getCredentialRequestEncryptionEncValuesSupported(); + credentialRequestEncryptionZipValuesSupported = metadata.getCredentialRequestEncryptionZipValuesSupported(); + requireCredentialRequestEncryption = metadata.isRequireCredentialRequestEncryption(); credentialResponseEncryptionAlgValuesSupported = metadata.getCredentialResponseEncryptionAlgValuesSupported(); credentialResponseEncryptionEncValuesSupported = metadata.getCredentialResponseEncryptionEncValuesSupported(); + credentialResponseEncryptionZipValuesSupported = metadata.getCredentialResponseEncryptionZipValuesSupported(); requireCredentialResponseEncryption = metadata.isRequireCredentialResponseEncryption(); + batchSize = metadata.getBatchSize(); + display = metadata.getDisplay(); credentialsSupported = metadata.getCredentialsSupported(); } @@ -323,6 +484,11 @@ public CredentialIssuerMetadata setCredentialEndpoint(URI endpoint) * this property should be omitted. *
* + *+ * NOTE: This metadata parameter was deprecated and is not available in the + * 1.0-Final version of the OID4VCI specification. + *
+ * * @return * The URL of the batch credential endpoint. */ @@ -341,10 +507,16 @@ public URI getBatchCredentialEndpoint() * this property should be omitted. * * + *+ * NOTE: This metadata parameter was deprecated and is not available in the + * 1.0-Final version of the OID4VCI specification. + *
+ * * @param endpoint * The URL of the batch credential endpoint. * * @return + * {@code this} object. */ public CredentialIssuerMetadata setBatchCredentialEndpoint(URI endpoint) { @@ -399,6 +571,284 @@ public CredentialIssuerMetadata setDeferredCredentialEndpoint(URI endpoint) } + /** + * Get the URL of the notification endpoint. This property corresponds to + * the {@code notification_endpoint} metadata. + * + * @return + * The URL of the notification endpoint. + * + * @since 4.26 + */ + public URI getNotificationEndpoint() + { + return notificationEndpoint; + } + + + /** + * Set the URL of the notification endpoint. This property corresponds to + * the {@code notification_endpoint} metadata. + * + * @param endpoint + * The URL of the notification endpoint. + * + * @return + * {@code this} object. + * + * @since 4.26 + */ + public CredentialIssuerMetadata setNotificationEndpoint(URI endpoint) + { + this.notificationEndpoint = endpoint; + + return this; + } + + + /** + * Get the URL of the nonce endpoint. This property corresponds to the + * {@code nonce_endpoint} metadata. + * + *+ * NOTE: This metadata parameter exists in the 1.0-Final version of the + * OID4VCI specification but is not available in the 1.0-ID1 version. + *
+ * + * @return + * The URL of the nonce endpoint. + * + * @since 4.26 + */ + public URI getNonceEndpoint() + { + return nonceEndpoint; + } + + + /** + * Set the URL of the nonce endpoint. This property corresponds to the + * {@code nonce_endpoint} metadata. + * + *+ * NOTE: This metadata parameter exists in the 1.0-Final version of the + * OID4VCI specification but is not available in the 1.0-ID1 version. + *
+ * + * @param endpoint + * The URL of the nonce endpoint. + * + * @return + * {@code this} object. + * + * @since 4.26 + */ + public CredentialIssuerMetadata setNonceEndpoint(URI endpoint) + { + this.nonceEndpoint = endpoint; + + return this; + } + + + /** + * Get the JWK Set for credential request encryption. + * + *+ * The public part of this JWK Set is used as the value of the + * {@code credential_request_encryption.jwks} metadata. + *
+ * + *+ * NOTE: This metadata parameter exists in the 1.0-Final version of the + * OID4VCI specification but is not available in the 1.0-ID1 version. + *
+ * + * @return + * The JWK Set for credential request encryption. + * + * @since 4.26 + */ + public String getCredentialRequestEncryptionJwks() + { + return credentialRequestEncryptionJwks; + } + + + /** + * Set the JWK Set for credential request encryption. + * + *+ * The public part of this JWK Set is used as the value of the + * {@code credential_request_encryption.jwks} metadata. + *
+ * + *+ * NOTE: This metadata parameter exists in the 1.0-Final version of the + * OID4VCI specification but is not available in the 1.0-ID1 version. + *
+ * + * @param jwks + * The JWK Set for credential request encryption. + * + * @return + * {@code this} object. + * + * @since 4.26 + */ + public CredentialIssuerMetadata setCredentialRequestEncryptionJwks(String jwks) + { + this.credentialRequestEncryptionJwks = jwks; + + return this; + } + + + /** + * Get the supported JWE {@code enc} algorithms for credential request + * encryption. This property corresponds to the + * {@code credential_request_encryption.enc_values_supported} metadata. + * + *+ * NOTE: This metadata parameter exists in the 1.0-Final version of the + * OID4VCI specification but is not available in the 1.0-ID1 version. + *
+ * + * @return + * The supported JWE {@code enc} algorithms for credential request + * encryption. + * + * @since 4.26 + */ + public JWEEnc[] getCredentialRequestEncryptionEncValuesSupported() + { + return credentialRequestEncryptionEncValuesSupported; + } + + + /** + * Set the supported JWE {@code enc} algorithms for credential request + * encryption. This property corresponds to the + * {@code credential_request_encryption.enc_values_supported} metadata. + * + *+ * NOTE: This metadata parameter exists in the 1.0-Final version of the + * OID4VCI specification but is not available in the 1.0-ID1 version. + *
+ * + * @param encs + * The supported JWE {@code enc} algorithms for credential request + * encryption. + * + * @return + * {@code this} object. + * + * @since 4.26 + */ + public CredentialIssuerMetadata setCredentialRequestEncryptionEncValuesSupported(JWEEnc[] encs) + { + this.credentialRequestEncryptionEncValuesSupported = encs; + + return this; + } + + + /** + * Get the supported JWE {@code zip} algorithms for credential request + * encryption. This property corresponds to the + * {@code credential_request_encryption.zip_values_supported} metadata. + * + *+ * NOTE: This metadata parameter exists in the 1.0-Final version of the + * OID4VCI specification but is not available in the 1.0-ID1 version. + *
+ * + * @return + * The supported JWE {@code zip} algorithms for credential request + * encryption. + * + * @since 4.26 + */ + public JWEZip[] getCredentialRequestEncryptionZipValuesSupported() + { + return credentialRequestEncryptionZipValuesSupported; + } + + + /** + * Set the supported JWE {@code zip} algorithms for credential request + * encryption. This property corresponds to the + * {@code credential_request_encryption.zip_values_supported} metadata. + * + *+ * NOTE: This metadata parameter exists in the 1.0-Final version of the + * OID4VCI specification but is not available in the 1.0-ID1 version. + *
+ * + * @param zips + * The supported JWE {@code zip} algorithms for credential request + * encryption. + * + * @return + * {@code this} object. + * + * @since 4.26 + */ + public CredentialIssuerMetadata setCredentialRequestEncryptionZipValuesSupported(JWEZip[] zips) + { + this.credentialRequestEncryptionZipValuesSupported = zips; + + return this; + } + + + /** + * Get the boolean flag indicating whether credential request encryption + * is required. This property corresponds to the + * {@code credential_request_encryption.encryption_required} metadata. + * + *+ * If this flag is {@code true}, every credential request to the credential + * issuer must be encrypted. + *
+ * + * @return + * {@code true} if credential request encryption is required. + * + * @since 4.26 + */ + public boolean isRequireCredentialRequestEncryption() + { + return requireCredentialRequestEncryption; + } + + + /** + * Set the boolean flag indicating whether credential request encryption + * is required. This property corresponds to the + * {@code credential_request_encryption.encryption_required} metadata. + * + *+ * If this flag is {@code true}, every credential request to the credential + * issuer must be encrypted + *
+ * + * @param required + * {@code true} to require credential request encryption. + * + * @return + * {@code this} object. + * + * @since 4.26 + */ + public CredentialIssuerMetadata setRequireCredentialRequestEncryption(boolean required) + { + this.requireCredentialRequestEncryption = required; + + return this; + } + + /** * Get the supported JWE {@code alg} algorithms for credential response * encryption. This property corresponds to the @@ -478,6 +928,55 @@ public CredentialIssuerMetadata setCredentialResponseEncryptionEncValuesSupporte } + /** + * Get the supported JWE {@code zip} algorithms for credential response + * encryption. This property corresponds to the + * {@code credential_response_encryption.zip_values_supported} metadata. + * + *+ * NOTE: This metadata parameter exists in the 1.0-Final version of the + * OID4VCI specification but is not available in the 1.0-ID1 version. + *
+ * + * @return + * The supported JWE {@code zip} algorithms for credential response + * encryption. + * + * @since 4.26 + */ + public JWEZip[] getCredentialResponseEncryptionZipValuesSupported() + { + return credentialResponseEncryptionZipValuesSupported; + } + + + /** + * Set the supported JWE {@code zip} algorithms for credential response + * encryption. This property corresponds to the + * {@code credential_response_encryption.zip_values_supported} metadata. + * + *+ * NOTE: This metadata parameter exists in the 1.0-Final version of the + * OID4VCI specification but is not available in the 1.0-ID1 version. + *
+ * + * @param zips + * The supported JWE {@code zip} algorithms for credential response + * encryption. + * + * @return + * {@code this} object. + * + * @since 4.26 + */ + public CredentialIssuerMetadata setCredentialResponseEncryptionZipValuesSupported(JWEZip[] zips) + { + this.credentialResponseEncryptionZipValuesSupported = zips; + + return this; + } + + /** * Get the boolean flag indicating whether credential response encryption * is required. This property corresponds to the @@ -525,6 +1024,152 @@ public CredentialIssuerMetadata setRequireCredentialResponseEncryption(boolean r } + /** + * Get the maximum array size for the {@code proofs} parameter in a + * credential request. This property corresponds to the + * {@code batch_credential_issuance.batch_size} metadata. + * + *+ * If the value of this property is 2 or greater, the + * {@code batch_credential_issuance} parameter will appear in the response + * from the credential metadata endpoint. + *
+ * + *+ * NOTE: This metadata parameter exists in the 1.0-Final version of the + * OID4VCI specification but is not available in the 1.0-ID1 version. + *
+ * + * @return + * The maximum array size for the {@code proofs} parameter in a + * credential request. + * + * @since 4.26 + */ + public int getBatchSize() + { + return batchSize; + } + + + /** + * Set the maximum array size for the {@code proofs} parameter in a + * credential request. This property corresponds to the + * {@code batch_credential_issuance.batch_size} metadata. + * + *+ * If the value of this property is 2 or greater, the + * {@code batch_credential_issuance} parameter will appear in the response + * from the credential metadata endpoint. + *
+ * + *+ * NOTE: This metadata parameter exists in the 1.0-Final version of the + * OID4VCI specification but is not available in the 1.0-ID1 version. + *
+ * + * @param batchSize + * The maximum array size for the {@code proofs} parameter in a + * credential request. + * + * @return + * {@code this} object. + * + * @since 4.26 + */ + public CredentialIssuerMetadata setBatchSize(int batchSize) + { + this.batchSize = batchSize; + + return this; + } + + + /** + * Get the display properties of this credential issuer in JSON array format. + * + *+ * The following is an example of {@code display} value excerpted from Appendix I.1. Credential IssuerMetadata of the OID4VCI specification. + *
+ * + *
+ * [
+ * {
+ * "name": "Example University",
+ * "locale": "en-US",
+ * "logo": {
+ * "uri": "https://university.example.edu/public/logo.png",
+ * "alt_text":"a square logo of a university"
+ * }
+ * },
+ * {
+ * "name": "Example Université",
+ * "locale": "fr-FR",
+ * "logo": {
+ * "uri": "https://university.example.edu/public/logo.png",
+ * "alt_text":"Un logo universitaire carré"
+ * }
+ * }
+ * ]
+ *
+ * @return
+ * The display properties.
+ *
+ * @since 4.26
+ */
+ public String getDisplay()
+ {
+ return display;
+ }
+
+
+ /**
+ * Set the display properties of this credential issuer in JSON array format.
+ *
+ * + * The following is an example of {@code display} value excerpted from Appendix I.1. Credential IssuerMetadata of the OID4VCI specification. + *
+ * + *
+ * [
+ * {
+ * "name": "Example University",
+ * "locale": "en-US",
+ * "logo": {
+ * "uri": "https://university.example.edu/public/logo.png",
+ * "alt_text":"a square logo of a university"
+ * }
+ * },
+ * {
+ * "name": "Example Université",
+ * "locale": "fr-FR",
+ * "logo": {
+ * "uri": "https://university.example.edu/public/logo.png",
+ * "alt_text":"Un logo universitaire carré"
+ * }
+ * }
+ * ]
+ *
+ * @param display
+ * The display properties.
+ *
+ * @return
+ * {@code this} object.
+ *
+ * @since 4.26
+ */
+ public CredentialIssuerMetadata setDisplay(String display)
+ {
+ this.display = display;
+
+ return this;
+ }
+
+
/**
* Get the information about supported credential configurations in the
* JSON format. This property corresponds to the
@@ -609,8 +1254,15 @@ public boolean isEmpty()
(credentialEndpoint == null) &&
(batchCredentialEndpoint == null) &&
(deferredCredentialEndpoint == null) &&
+ (notificationEndpoint == null) &&
+ (nonceEndpoint == null) &&
+ (credentialRequestEncryptionJwks == null) &&
+ (credentialRequestEncryptionEncValuesSupported == null) &&
+ (credentialRequestEncryptionZipValuesSupported == null) &&
(credentialResponseEncryptionAlgValuesSupported == null) &&
(credentialResponseEncryptionEncValuesSupported == null) &&
+ (credentialResponseEncryptionZipValuesSupported == null) &&
+ (display == null) &&
(credentialsSupported == null);
}
@@ -696,20 +1348,60 @@ public Map+ * The Authlete API is supposed to be used from within the implementation of + * the nonce endpoint of the credential issuer. + *
+ * + * @since 4.27 + * @since Authlete 3.0.22 + * + * @see + * OpenID for Verifiable Credential Issuance 1.0, + * Section 7. Nonce Endpoint + */ +public class CredentialNonceRequest implements Serializable +{ + private static final long serialVersionUID = 1L; + + + private boolean pretty; + + + + /** + * Get the flag indicating whether the nonce response is written in the pretty + * format or not. + * + * @return + * {@code true} if the nonce response is written in the pretty format. + */ + public boolean isPretty() + { + return pretty; + } + + + /** + * Set the flag indicating whether the nonce response is written in the pretty + * format or not. + * + * @param pretty + * {@code true} to write the nonce response in the pretty format. + * + * @return + * {@code this} object. + */ + public CredentialNonceRequest setPretty(boolean pretty) + { + this.pretty = pretty; + + return this; + } +} diff --git a/src/main/java/com/authlete/common/dto/CredentialNonceResponse.java b/src/main/java/com/authlete/common/dto/CredentialNonceResponse.java new file mode 100644 index 0000000000..4bd7c91070 --- /dev/null +++ b/src/main/java/com/authlete/common/dto/CredentialNonceResponse.java @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2025 Authlete, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.authlete.common.dto; + + +/** + * Response from Authlete's {@code /vci/nonce} API. + * + *+ * The Authlete API is supposed to be used from within the implementation + * the nonce endpoint of the credential issuer. + *
+ * + *
+ * Authlete's {@code /vci/nonce} API returns JSON which can be mapped to
+ * this class. The credential issuer implementation should retrieve the value
+ * of the {@link #getAction() action} response parameter from
+ * the API response and take the following steps according to the value.
+ *
OK
+ * When the value of the {@link #getAction() action} response
+ * parameter is {@link Action#OK OK}, it means that Authlete
+ * could prepare a nonce response successfully.
+ *
+ * + *
+ * In this case, the implementation of the nonce endpoint of the credential + * issuer should return an HTTP response with the HTTP status code + * "{@code 200 OK}" and the content type "{@code application/json}". The + * message body of the response has been prepared by Authlete's {@code /vci/nonce} + * API and it is available as the + *{@link #getResponseContent() responseContent} response parameter.
+ *
+ *
+ * + * The implementation of the nonce endpoint can construct an HTTP response by + * doing like below. + *
+ * + *
+ * 200 OK
+ * Content-Type: application/json
+ * Cache-Control: no-store
+ * (Other HTTP headers)
+ *
+ * (the value of the {@link #getResponseContent() responseContent} response parameter)
+ *
+ * + * Note that the OpenID for Verifiable Credential Issuance 1.0 specification requires + * the response to include the {@code Cache-Control} header including the + * value {@code no-store}. + *
+ * + *NOT_FOUND
+ * When the value of the {@link #getAction() action} response
+ * parameter is {@link Action#NOT_FOUND NOT_FOUND}, it means that
+ * the service configuration has not enabled the feature of Verifiable
+ * Credentials and so the nonce endpoint should not be accessed.
+ *
{@link #getResponseContent() responseContent} response
+ * parameter.
+ *
+ *
+ * + * The implementation of the nonce endpoint can construct an HTTP response by + * doing like below. + *
+ * + *
+ * 404 Not Found
+ * Content-Type: application/json
+ * (Other HTTP headers)
+ *
+ * (the value of the {@link #getResponseContent() responseContent} response parameter)
+ *
+ * INTERNAL_SERVER_ERROR
+ * When the value of the {@link #getAction() action} response
+ * parameter is {@link Action#INTERNAL_SERVER_ERROR INTERNAL_SERVER_ERROR},
+ * it means that an unexpected error has occurred on Authlete side or the
+ * service has not been set up properly yet.
+ *
{@link #getResponseContent()
+ * responseContent} response parameter.
+ *
+ *
+ * + * Such simple implementation of the nonce endpoint can construct an HTTP response + * by doing like below. + *
+ * + *
+ * 500 Internal Server Error
+ * Content-Type: application/json
+ * (Other HTTP headers)
+ *
+ * (the value of the {@link #getResponseContent() responseContent} response parameter)
+ *
+ * + * However, in real commercial deployments, it is rare for a credential issuer + * to return "{@code 500 Internal Server Error}" when it encounters an + * unexpected internal error. It's up to implementations of credential issuers + * what they actually return in the case of internal server error. + *
+ * + * @since 4.27 + * @since Authlete 3.0.22 + * + * @see + * OpenID for Verifiable Credential Issuance 1.0 + */ +public class CredentialNonceResponse extends ApiResponse +{ + private static final long serialVersionUID = 1L; + + + /** + * The next action that the implementation of the nonce endpoint + * should take after getting a response from Authlete's + * {@code /vci/nonce} API. + * + * @since 4.27 + * @since Authlete 3.0.22 + */ + public enum Action + { + /** + * A nonce response has been prepared successfully. The implementation + * of the nonce endpoint should return an HTTP response with the HTTP + * status code "{@code 200 OK}" and the content type + * "{@code application/json}". + */ + OK, + + /** + * The feature of Verifiable Credentials is not enabled. The + * implementation of the nonce endpoint should return an HTTP response + * with the HTTP status code "{@code 404 Not Found}" and the content + * type "{@code application/json}" to indicate that the endpoint should + * not be accessed. + */ + NOT_FOUND, + + /** + * An unexpected error occurred on Authlete side or the service has + * not been set up properly yet. A simple implementation of the nonce + * endpoint would return an HTTP response with the HTTP status code + * "{@code 500 Internal Server Error}" and the content type + * "{@code application/json}". + */ + INTERNAL_SERVER_ERROR, + } + + + private Action action; + private String responseContent; + private String cnonce; + + + /** + * Get the next action that the implementation of the nonce endpoint should + * take after getting a response from Authlete's {@code /vci/nonce} API. + * + * @return + * The next action. + */ + public Action getAction() + { + return action; + } + + + /** + * Set the next action that the implementation of the nonce endpoint should + * take after getting a response from Authlete's {@code /vci/nonce} API. + * + * @param action + * The next action. + * + * @return + * {@code this} object. + */ + public CredentialNonceResponse setAction(Action action) + { + this.action = action; + + return this; + } + + + /** + * Get the content that the implementation of the nonce endpoint should use + * when it constructs a response. + * + * @return + * The response content in the JSON format. + */ + public String getResponseContent() + { + return responseContent; + } + + + /** + * Set the content that the implementation of the nonce endpoint should use + * when it constructs a response. + * + * @param content + * The response content in the JSON format. + * + * @return + * {@code this} object. + */ + public CredentialNonceResponse setResponseContent(String content) + { + this.responseContent = content; + + return this; + } + + + /** + * Get the {@code c_nonce} value generated by the {@code /vci/nonce} API. + * + * @return + * The generated {@code c_nonce} value. + */ + public String getCnonce() + { + return cnonce; + } + + + /** + * Set the {@code c_nonce} value generated by the {@code /vci/nonce} API. + * + * @param cnonce + * The generated {@code c_nonce} value. + * + * @return + * {@code this} object. + */ + public CredentialNonceResponse setCnonce(String cnonce) + { + this.cnonce = cnonce; + + return this; + } +} diff --git a/src/main/java/com/authlete/common/dto/CredentialRequestInfo.java b/src/main/java/com/authlete/common/dto/CredentialRequestInfo.java index 97644f91bd..7f0a6d020e 100644 --- a/src/main/java/com/authlete/common/dto/CredentialRequestInfo.java +++ b/src/main/java/com/authlete/common/dto/CredentialRequestInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2024 Authlete, Inc. + * Copyright (C) 2023-2026 Authlete, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -32,12 +32,12 @@ * @since 3.66 * @since Authlete 3.0 * - * @see OpenID for Verifiable Credential Issuance + * @see + * OpenID for Verifiable Credential Issuance 1.0 */ public class CredentialRequestInfo implements Serializable { - private static final long serialVersionUID = 2L; + private static final long serialVersionUID = 3L; /** @@ -72,6 +72,25 @@ public class CredentialRequestInfo implements Serializable private String details; + /** + * The {@code credential_configuration_id} parameter in the credential + * request. + * + * @since 4.34 + * @since Authlete 3.0.25 + */ + private String credentialConfigurationId; + + + /** + * The {@code credential_identifier} parameter in the credential request. + * + * @since 4.34; + * @since Authlete 3.0.25 + */ + private String credentialIdentifier; + + /** * Get the identifier of the credential request. * @@ -118,9 +137,18 @@ public CredentialRequestInfo setIdentifier(String identifier) /** * Get the value of the {@code format} parameter in the credential request. * + *+ * This {@code format} property is available only when the supported OID4VCI + * specification version is {@code "1.0-ID1"} (i.e., when the Service's + * {@code oid4vciVersion} is unset or set to {@code "1.0-ID1"}) and the + * credential request contains the {@code format} parameter. Note that in + * the final version of the specification, the {@code format} parameter is + * deprecated. + *
+ * * @return * The value of the {@code format} parameter in the credential request. - * Such as {@code "vc+sd-jwt"}. + * Such as {@code "dc+sd-jwt"}. */ public String getFormat() { @@ -131,9 +159,18 @@ public String getFormat() /** * Set the value of the {@code format} parameter in the credential request. * + *+ * This {@code format} property is available only when the supported OID4VCI + * specification version is {@code "1.0-ID1"} (i.e., when the Service's + * {@code oid4vciVersion} is unset or set to {@code "1.0-ID1"}) and the + * credential request contains the {@code format} parameter. Note that in + * the final version of the specification, the {@code format} parameter is + * deprecated. + *
+ * * @param format * The value of the {@code format} parameter in the credential request. - * Such as {@code "jwt_vc_json"} and {@code "vc+sd-jwt"}. + * Such as {@code "jwt_vc_json"} and {@code "dc+sd-jwt"}. * * @return * {@code this} object. @@ -233,11 +270,18 @@ public CredentialRequestInfo setBindingKeys(String[] bindingKeys) * ** The value is almost the same as the credential request except that it - * does not contain the {@code "format"} parameter, the {@code "proof"} - * parameter, the {@code "proofs"} parameter, and the - * {@code credential_response_encryption} parameter. + * does not contain the following parameters: *
* + ** For example, when the original credential request holds the following: *
@@ -299,4 +343,114 @@ public CredentialRequestInfo setDetails(String details) return this; } + + + /** + * Get the value of the {@code credential_configuration_id} parameter in + * the credential request. + * + *+ * This property is not available when the supported OID4VCI specification + * version is {@code "1.0-ID1"} (i.e., when the Service's + * {@code oid4vciVersion} is unset or set to {@code "1.0-ID1"}), because + * the {@code credential_configuration_id} parameter does not exist in + * OID4VCI 1.0 ID1. + *
+ * + * @return + * The value of the {@code credential_configuration_id} parameter + * in the credential request. + * + * @since 4.34 + * @since Authlete 3.0.25 + */ + public String getCredentialConfigurationId() + { + return credentialConfigurationId; + } + + + /** + * Set the value of the {@code credential_configuration_id} parameter in + * the credential request. + * + *+ * This property is not available when the supported OID4VCI specification + * version is {@code "1.0-ID1"} (i.e., when the Service's + * {@code oid4vciVersion} is unset or set to {@code "1.0-ID1"}), because + * the {@code credential_configuration_id} parameter does not exist in + * OID4VCI 1.0 ID1. + *
+ * + * @param id + * The value of the {@code credential_configuration_id} parameter + * in the credential request. + * + * @return + * {@code this} object. + * + * @since 4.34 + * @since Authlete 3.0.25 + */ + public CredentialRequestInfo setCredentialConfigurationId(String id) + { + this.credentialConfigurationId = id; + + return this; + } + + + /** + * Get the value of the {@code credential_identifier} parameter in the + * credential request. + * + *+ * This property is not available when the supported OID4VCI specification + * version is {@code "1.0-ID1"} (i.e., when the Service's + * {@code oid4vciVersion} is unset or set to {@code "1.0-ID1"}), because + * Authlete does not support the {@code credential_identifier} parameter + * for OID4VCI 1.0 ID1. + *
+ * + * @return + * The value of the {@code credential_identifier} parameter in the + * credential request. + * + * @since 4.34 + * @since Authlete 3.0.25 + */ + public String getCredentialIdentifier() + { + return credentialIdentifier; + } + + + /** + * Set the value of the {@code credential_identifier} parameter in the + * credential request. + * + *+ * This property is not available when the supported OID4VCI specification + * version is {@code "1.0-ID1"} (i.e., when the Service's + * {@code oid4vciVersion} is unset or set to {@code "1.0-ID1"}), because + * Authlete does not support the {@code credential_identifier} parameter + * for OID4VCI 1.0 ID1. + *
+ * + * @param identifier + * The value of the {@code credential_identifier} parameter in the + * credential request. + * + * @return + * {@code this} object. + * + * @since 4.34 + * @since Authlete 3.0.25 + */ + public CredentialRequestInfo setCredentialIdentifier(String identifier) + { + this.credentialIdentifier = identifier; + + return this; + } } diff --git a/src/main/java/com/authlete/common/dto/CredentialSingleIssueRequest.java b/src/main/java/com/authlete/common/dto/CredentialSingleIssueRequest.java index a1a36d61af..e116ca52c3 100644 --- a/src/main/java/com/authlete/common/dto/CredentialSingleIssueRequest.java +++ b/src/main/java/com/authlete/common/dto/CredentialSingleIssueRequest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 Authlete, Inc. + * Copyright (C) 2023-2026 Authlete, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -26,7 +26,7 @@ * The Authlete API is supposed to be called by the implementation of the * credential endpoint. The endpoint is defined in the "OpenID for Verifiable Credential Issuance" (OID4VCI) specification. + * >OpenID for Verifiable Credential Issuance 1.0" (OID4VCI) specification. * * *@@ -49,12 +49,12 @@ * @since 3.67 * @since Authlete 3.0 * - * @see OpenID for Verifiable Credential Issuance + * @see + * OpenID for Verifiable Credential Issuance 1.0 */ public class CredentialSingleIssueRequest implements Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 2L; /** @@ -69,6 +69,26 @@ public class CredentialSingleIssueRequest implements Serializable private CredentialIssuanceOrder order; + /** + * The flag that indicates whether to deny the credential request. + * + * @since 4.35 + * @since Authlete 3.0.25 + */ + private boolean denied; + + + /** + * The minimum amount of time in seconds that the Wallet SHOULD wait after + * receiving the response before sending a new request to the Deferred + * Credential Endpoint. + * + * @since 4.35 + * @since Authlete 3.0.25 + */ + private int interval; + + /** * Get the access token that was presented at the credential endpoint. * @@ -127,4 +147,158 @@ public CredentialSingleIssueRequest setOrder(CredentialIssuanceOrder order) return this; } + + + /** + * Get the flag that indicates whether to deny the credential request. + * + *
+ * If this parameter is set to {@code true}, the response content prepared + * by Authlete will contain {@code "error":"credential_request_denied"} + * like below. + *
+ * + *
+ * {
+ * "error": "credential_request_denied"
+ * }
+ *
+ *
+ * + * Note that the {@code credential_request_denied} error code does not + * exist in OID4VCI 1.0 ID1. + *
+ * + * @return + * {@code true} for generating a response content containing + * {@code "error":"credential_request_denied"}. + * + * @since 4.35 + * @since Authlete 3.0.25 + */ + public boolean isDenied() + { + return denied; + } + + + /** + * Set the flag that indicates whether to deny the credential request. + * + *+ * If this parameter is set to {@code true}, the response content prepared + * by Authlete will contain {@code "error":"credential_request_denied"} + * like below. + *
+ * + *
+ * {
+ * "error": "credential_request_denied"
+ * }
+ *
+ *
+ * + * Note that the {@code credential_request_denied} error code does not + * exist in OID4VCI 1.0 ID1. + *
+ * + * @param denied + * {@code true} for generating a response content containing + * {@code "error":"credential_request_denied"}. + * + * @return + * {@code this} object. + * + * @since 4.35 + * @since Authlete 3.0.25 + */ + public CredentialSingleIssueRequest setDenied(boolean denied) + { + this.denied = denied; + + return this; + } + + + /** + * Get the minimum amount of time in seconds that the Wallet SHOULD wait + * after receiving the response before sending a new request to the + * Deferred Credential Endpoint. + * + *+ * The value of this parameter is used as the value of the {@code interval} + * parameter in the credential response. However, the {@code interval} + * parameter is included in the credential response only when the following + * conditions are met: + *
+ * + *+ * The value of this parameter is used as the value of the {@code interval} + * parameter in the credential response. However, the {@code interval} + * parameter is included in the credential response only when the following + * conditions are met: + *
+ * + *+ * Since Authlete 3.0.25, this action is returned also when the + * credential issuer has decided not to issue a credential for the + * presented transaction ID (that is, when the {@code /vci/single/issue} + * API is called with {@code denied} set to {@code true}). + *
+ * * @since 3.86 */ BAD_REQUEST, diff --git a/src/main/java/com/authlete/common/dto/CursorResponse.java b/src/main/java/com/authlete/common/dto/CursorResponse.java new file mode 100644 index 0000000000..6683c58c42 --- /dev/null +++ b/src/main/java/com/authlete/common/dto/CursorResponse.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2014 Authlete, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.authlete.common.dto; + + +import java.io.Serializable; + + +/** + * Response from Authlete's cursor based APIs. + * + * @author Kyle Gonzalez + * @since Authlete 3.0.34 + */ +public class CursorResponse+ * Since version 3.0.28, Authlete recognizes the {@code challenge} claim in + * Client Attestation PoP JWTs. If the {@code challenge} claim is required + * (= if the service's challenge endpoint is configured), the Authlete API + * checks whether the {@code challenge} claim in the presented Client + * Attestation PoP JWT is identical to the expected value. + *
+ * + *+ * If the {@code attestationChallenge} response parameter from the API is not + * null, its value is the expected attestation challenge value for Client + * Attestation PoP JWT. The expected value needs to be conveyed to the client + * application as the value of the {@code OAuth-Client-Attestation-Challenge} + * HTTP header. + *
+ * + *OAuth-Client-Attestation-Challenge: (The value returned from {@link #getAttestationChallenge()})
+ *
+ * + * See OAuth 2.0 Attestation-Based Client Authentication for details. + *
+ * * @since 2.42 */ public class DeviceAuthorizationResponse extends ApiResponse { - private static final long serialVersionUID = 9L; + private static final long serialVersionUID = 11L; /** @@ -249,6 +277,22 @@ public enum Action */ private boolean clientEntityIdUsed; + /** + * The location of the client's metadata document. + * + * @since 4.29 + * @since Authlete 3.0.22 + */ + private URI metadataDocumentLocation; + + /** + * Whether the client's metadata document was used. + * + * @since 4.29 + * @since Authlete 3.0.22 + */ + private boolean metadataDocumentUsed; + /** * @since Authlete 2.0.0 */ @@ -354,6 +398,16 @@ public enum Action */ private String[] warnings; + /** + * The expected attestation challenge value for Client Attestation PoP JWT, + * which should be used as the value of the + * {@code OAuth-Client-Attestation-Challenge} HTTP header. + * + * @since 4.39 + * @since Authlete 3.0.28 + */ + private String attestationChallenge; + /** * Get the next action that the implementation of the device authorization @@ -622,17 +676,128 @@ public DeviceAuthorizationResponse setClientEntityIdUsed(boolean used) /** - * Get the client identifier used in the device authorization request. + * Get the location of the client's metadata document. This property + * holds a non-null value only when {@link #isMetadataDocumentUsed()} + * returns {@code true}. + * + * @return + * The location of the client's metadata document. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public URI getMetadataDocumentLocation() + { + return metadataDocumentLocation; + } + + + /** + * Set the location of the client's metadata document. This property + * should hold a non-null value only when {@link #isMetadataDocumentUsed()} + * returns {@code true}. + * + * @param location + * The location of the client's metadata document. + * + * @return + * {@code this} object. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public DeviceAuthorizationResponse setMetadataDocumentLocation(URI location) + { + this.metadataDocumentLocation = location; + + return this; + } + + + /** + * Get the flag which indicates whether the location of the client's + * metadata document was used as a client ID. + * + *+ * This can happen when the service supports OAuth Client ID Metadata Document. + * (cf. {@link Service#isClientIdMetadataDocumentSupported()}) + *
+ * + * @return + * {@code true} if the location of client's metadata document + * was used as a client ID. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public boolean isMetadataDocumentUsed() + { + return metadataDocumentUsed; + } + + + /** + * Set the flag which indicates whether the location of the client's + * metadata document was used as a client ID. * *- * When {@link #isClientIdAliasUsed()} returns {@code true}, this method - * returns the same value as {@link #getClientIdAlias()} does. Otherwise, - * if {@link #isClientEntityIdUsed()} returns {@code true}, this method - * returns the same value as {@link #getClientEntityId()}{@code .toString()} - * does. In other cases, this method returns the string representation of - * the value returned from {@link #getClientId()}. + * This can happen when the service supports OAuth Client ID Metadata Document. + * (cf. {@link Service#isClientIdMetadataDocumentSupported()}) *
* + * @param used + * {@code true} to indicate that the location of the client's + * metadata document was used as a client ID. + * + * @return + * {@code this} object. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public DeviceAuthorizationResponse setMetadataDocumentUsed(boolean used) + { + this.metadataDocumentUsed = used; + + return this; + } + + + /** + * Get the client identifier used in the device authorization request. + * + *+ * When this response parameter is not null, the implementation of the + * device authorization endpoint should add the + * {@code OAuth-Client-Attestation-Challenge} HTTP header in the response + * from the endpoint to the client application, using the value of this + * response parameter as the value of the HTTP header. + *
+ * + *
+ * OAuth-Client-Attestation-Challenge: (The value of this {@code attestationChallenge} response parameter)
+ *
+ *
+ * @return
+ * The expected attestation challenge value for Client Attestation
+ * PoP JWT.
+ *
+ * @since 4.39
+ * @since Authlete 3.0.28
+ *
+ * @see
+ * OAuth 2.0 Attestation-Based Client Authentication
+ */
+ public String getAttestationChallenge()
+ {
+ return attestationChallenge;
+ }
+
+
+ /**
+ * Set the expected attestation challenge value for Client Attestation PoP
+ * JWT, which should be used as the value of the
+ * {@code OAuth-Client-Attestation-Challenge} HTTP header.
+ *
+ * + * When this response parameter is not null, the implementation of the + * device authorization endpoint should add the + * {@code OAuth-Client-Attestation-Challenge} HTTP header in the response + * from the endpoint to the client application, using the value of this + * response parameter as the value of the HTTP header. + *
+ * + *
+ * OAuth-Client-Attestation-Challenge: (The value of this {@code attestationChallenge} response parameter)
+ *
+ *
+ * @param challenge
+ * The expected attestation challenge value for Client Attestation
+ * PoP JWT.
+ *
+ * @return
+ * {@code this} object.
+ *
+ * @since 4.39
+ * @since Authlete 3.0.28
+ *
+ * @see
+ * OAuth 2.0 Attestation-Based Client Authentication
+ */
+ public DeviceAuthorizationResponse setAttestationChallenge(String challenge)
+ {
+ this.attestationChallenge = challenge;
+
+ return this;
+ }
}
diff --git a/src/main/java/com/authlete/common/dto/DeviceVerificationResponse.java b/src/main/java/com/authlete/common/dto/DeviceVerificationResponse.java
index 5487dda579..c8023dbaa2 100644
--- a/src/main/java/com/authlete/common/dto/DeviceVerificationResponse.java
+++ b/src/main/java/com/authlete/common/dto/DeviceVerificationResponse.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019-2023 Authlete, Inc.
+ * Copyright (C) 2019-2025 Authlete, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
@@ -76,7 +76,7 @@
*/
public class DeviceVerificationResponse extends ApiResponse
{
- private static final long serialVersionUID = 9L;
+ private static final long serialVersionUID = 10L;
/**
@@ -148,6 +148,22 @@ public enum Action
*/
private boolean clientEntityIdUsed;
+ /**
+ * The location of the client's metadata document.
+ *
+ * @since 4.29
+ * @since Authlete 3.0.22
+ */
+ private URI metadataDocumentLocation;
+
+ /**
+ * Whether the client's metadata document was used.
+ *
+ * @since 4.29
+ * @since Authlete 3.0.22
+ */
+ private boolean metadataDocumentUsed;
+
/**
* @since Authlete 2.0.0
*/
@@ -453,18 +469,129 @@ public DeviceVerificationResponse setClientEntityIdUsed(boolean used)
/**
- * Get the client identifier used in the device authorization request for
- * the user code.
+ * Get the location of the client's metadata document. This property
+ * holds a non-null value only when {@link #isMetadataDocumentUsed()}
+ * returns {@code true}.
+ *
+ * @return
+ * The location of the client's metadata document.
+ *
+ * @since 4.29
+ * @since Authlete 3.0.22
+ *
+ * @see
+ * OAuth Client ID Metadata Document
+ */
+ public URI getMetadataDocumentLocation()
+ {
+ return metadataDocumentLocation;
+ }
+
+
+ /**
+ * Set the location of the client's metadata document. This property
+ * should hold a non-null value only when {@link #isMetadataDocumentUsed()}
+ * returns {@code true}.
+ *
+ * @param location
+ * The location of the client's metadata document.
+ *
+ * @return
+ * {@code this} object.
+ *
+ * @since 4.29
+ * @since Authlete 3.0.22
+ *
+ * @see
+ * OAuth Client ID Metadata Document
+ */
+ public DeviceVerificationResponse setMetadataDocumentLocation(URI location)
+ {
+ this.metadataDocumentLocation = location;
+
+ return this;
+ }
+
+
+ /**
+ * Get the flag which indicates whether the location of the client's
+ * metadata document was used as a client ID.
+ *
+ * + * This can happen when the service supports OAuth Client ID Metadata Document. + * (cf. {@link Service#isClientIdMetadataDocumentSupported()}) + *
+ * + * @return + * {@code true} if the location of client's metadata document + * was used as a client ID. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public boolean isMetadataDocumentUsed() + { + return metadataDocumentUsed; + } + + + /** + * Set the flag which indicates whether the location of the client's + * metadata document was used as a client ID. * *- * When {@link #isClientIdAliasUsed()} returns {@code true}, this method - * returns the same value as {@link #getClientIdAlias()} does. Otherwise, - * if {@link #isClientEntityIdUsed()} returns {@code true}, this method - * returns the same value as {@link #getClientEntityId()}{@code .toString()} - * does. In other cases, this method returns the string representation of - * the value returned from {@link #getClientId()}. + * This can happen when the service supports OAuth Client ID Metadata Document. + * (cf. {@link Service#isClientIdMetadataDocumentSupported()}) *
* + * @param used + * {@code true} to indicate that the location of the client's + * metadata document was used as a client ID. + * + * @return + * {@code this} object. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public DeviceVerificationResponse setMetadataDocumentUsed(boolean used) + { + this.metadataDocumentUsed = used; + + return this; + } + + + /** + * Get the client identifier used in the device authorization request for + * the user code. + * + *+ * This can happen when the service supports OAuth Client ID Metadata Document. + * (cf. {@link Service#isClientIdMetadataDocumentSupported()}) + *
+ * + * @return + * {@code true} if the location of client's metadata document + * was used as a client ID. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public boolean isMetadataDocumentUsed() + { + return metadataDocumentUsed; + } + + + /** + * Set the flag which indicates whether the location of the client's + * metadata document was used as a client ID. + * + *+ * This can happen when the service supports OAuth Client ID Metadata Document. + * (cf. {@link Service#isClientIdMetadataDocumentSupported()}) + *
+ * + * @param used + * {@code true} to indicate that the location of the client's + * metadata document was used as a client ID. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public void setMetadataDocumentUsed(boolean used) + { + this.metadataDocumentUsed = used; + } + + /** * Get the client certificate thumbprint used to validate the access token. * diff --git a/src/main/java/com/authlete/common/dto/PushedAuthReqResponse.java b/src/main/java/com/authlete/common/dto/PushedAuthReqResponse.java index 467f1e7382..33b6997561 100644 --- a/src/main/java/com/authlete/common/dto/PushedAuthReqResponse.java +++ b/src/main/java/com/authlete/common/dto/PushedAuthReqResponse.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 Authlete, Inc. + * Copyright (C) 2019-2026 Authlete, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -262,11 +262,39 @@ * 2.0 Demonstrating Proof of Possession (DPoP) for details. * * + *+ * Since version 3.0.28, Authlete recognizes the {@code challenge} claim in + * Client Attestation PoP JWTs. If the {@code challenge} claim is required + * (= if the service's challenge endpoint is configured), the Authlete API + * checks whether the {@code challenge} claim in the presented Client + * Attestation PoP JWT is identical to the expected value. + *
+ * + *+ * If the {@code attestationChallenge} response parameter from the API is not + * null, its value is the expected attestation challenge value for Client + * Attestation PoP JWT. The expected value needs to be conveyed to the client + * application as the value of the {@code OAuth-Client-Attestation-Challenge} + * HTTP header. + *
+ * + *OAuth-Client-Attestation-Challenge: (The value returned from {@link #getAttestationChallenge()})
+ *
+ * + * See OAuth 2.0 Attestation-Based Client Authentication for details. + *
+ * * @since 2.51 */ public class PushedAuthReqResponse extends ApiResponse { - private static final long serialVersionUID = 2L; + private static final long serialVersionUID = 3L; /** @@ -353,6 +381,16 @@ public static enum Action */ private String dpopNonce; + /** + * The expected attestation challenge value for Client Attestation PoP JWT, + * which should be used as the value of the + * {@code OAuth-Client-Attestation-Challenge} HTTP header. + * + * @since 4.39 + * @since Authlete 3.0.28 + */ + private String attestationChallenge; + /** * Get the next action that the authorization server should take. @@ -549,6 +587,77 @@ public PushedAuthReqResponse setDpopNonce(String dpopNonce) } + /** + * Get the expected attestation challenge value for Client Attestation PoP + * JWT, which should be used as the value of the + * {@code OAuth-Client-Attestation-Challenge} HTTP header. + * + *+ * When this response parameter is not null, the implementation of the + * pushed authorization request endpoint should add the + * {@code OAuth-Client-Attestation-Challenge} HTTP header in the response + * from the endpoint to the client application, using the value of this + * response parameter as the value of the HTTP header. + *
+ * + *
+ * OAuth-Client-Attestation-Challenge: (The value of this {@code attestationChallenge} response parameter)
+ *
+ *
+ * @return
+ * The expected attestation challenge value for Client Attestation
+ * PoP JWT.
+ *
+ * @since 4.39
+ * @since Authlete 3.0.28
+ *
+ * @see
+ * OAuth 2.0 Attestation-Based Client Authentication
+ */
+ public String getAttestationChallenge()
+ {
+ return attestationChallenge;
+ }
+
+
+ /**
+ * Set the expected attestation challenge value for Client Attestation PoP
+ * JWT, which should be used as the value of the
+ * {@code OAuth-Client-Attestation-Challenge} HTTP header.
+ *
+ * + * When this response parameter is not null, the implementation of the + * pushed authorization request endpoint should add the + * {@code OAuth-Client-Attestation-Challenge} HTTP header in the response + * from the endpoint to the client application, using the value of this + * response parameter as the value of the HTTP header. + *
+ * + *
+ * OAuth-Client-Attestation-Challenge: (The value of this {@code attestationChallenge} response parameter)
+ *
+ *
+ * @param challenge
+ * The expected attestation challenge value for Client Attestation
+ * PoP JWT.
+ *
+ * @return
+ * {@code this} object.
+ *
+ * @since 4.39
+ * @since Authlete 3.0.28
+ *
+ * @see
+ * OAuth 2.0 Attestation-Based Client Authentication
+ */
+ public PushedAuthReqResponse setAttestationChallenge(String challenge)
+ {
+ this.attestationChallenge = challenge;
+
+ return this;
+ }
+
+
/**
* Get the summary of this instance.
*/
diff --git a/src/main/java/com/authlete/common/dto/RevocationResponse.java b/src/main/java/com/authlete/common/dto/RevocationResponse.java
index 9d1e963050..b97867cf38 100644
--- a/src/main/java/com/authlete/common/dto/RevocationResponse.java
+++ b/src/main/java/com/authlete/common/dto/RevocationResponse.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 Authlete, Inc.
+ * Copyright (C) 2015-2026 Authlete, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -199,6 +199,34 @@
*
*
*
+ * + * Since version 3.0.28, Authlete recognizes the {@code challenge} claim in + * Client Attestation PoP JWTs. If the {@code challenge} claim is required + * (= if the service's challenge endpoint is configured), the Authlete API + * checks whether the {@code challenge} claim in the presented Client + * Attestation PoP JWT is identical to the expected value. + *
+ * + *+ * If the {@code attestationChallenge} response parameter from the API is not + * null, its value is the expected attestation challenge value for Client + * Attestation PoP JWT. The expected value needs to be conveyed to the client + * application as the value of the {@code OAuth-Client-Attestation-Challenge} + * HTTP header. + *
+ * + *OAuth-Client-Attestation-Challenge: (The value returned from {@link #getAttestationChallenge()})
+ *
+ * + * See OAuth 2.0 Attestation-Based Client Authentication for details. + *
+ * * @see RFC 7009, OAuth 2.0 Token Revocation * * @author Takahiko Kawasaki @@ -208,7 +236,7 @@ */ public class RevocationResponse extends ApiResponse { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 2L; /** @@ -250,6 +278,16 @@ public enum Action private Action action; private String responseContent; + /** + * The expected attestation challenge value for Client Attestation PoP JWT, + * which should be used as the value of the + * {@code OAuth-Client-Attestation-Challenge} HTTP header. + * + * @since 4.39 + * @since Authlete 3.0.28 + */ + private String attestationChallenge; + /** * Get the next action that the service implementation should take. @@ -289,6 +327,70 @@ public void setResponseContent(String responseContent) } + /** + * Get the expected attestation challenge value for Client Attestation PoP + * JWT, which should be used as the value of the + * {@code OAuth-Client-Attestation-Challenge} HTTP header. + * + *+ * When this response parameter is not null, the implementation of the + * revocation endpoint should add the {@code OAuth-Client-Attestation-Challenge} + * HTTP header in the response from the endpoint to the client application, + * using the value of this response parameter as the value of the HTTP header. + *
+ * + *
+ * OAuth-Client-Attestation-Challenge: (The value of this {@code attestationChallenge} response parameter)
+ *
+ *
+ * @return
+ * The expected attestation challenge value for Client Attestation
+ * PoP JWT.
+ *
+ * @since 4.39
+ * @since Authlete 3.0.28
+ *
+ * @see
+ * OAuth 2.0 Attestation-Based Client Authentication
+ */
+ public String getAttestationChallenge()
+ {
+ return attestationChallenge;
+ }
+
+
+ /**
+ * Set the expected attestation challenge value for Client Attestation PoP
+ * JWT, which should be used as the value of the
+ * {@code OAuth-Client-Attestation-Challenge} HTTP header.
+ *
+ * + * When this response parameter is not null, the implementation of the + * revocation endpoint should add the {@code OAuth-Client-Attestation-Challenge} + * HTTP header in the response from the endpoint to the client application, + * using the value of this response parameter as the value of the HTTP header. + *
+ * + *
+ * OAuth-Client-Attestation-Challenge: (The value of this {@code attestationChallenge} response parameter)
+ *
+ *
+ * @param challenge
+ * The expected attestation challenge value for Client Attestation
+ * PoP JWT.
+ *
+ * @since 4.39
+ * @since Authlete 3.0.28
+ *
+ * @see
+ * OAuth 2.0 Attestation-Based Client Authentication
+ */
+ public void setAttestationChallenge(String challenge)
+ {
+ this.attestationChallenge = challenge;
+ }
+
+
/**
* Get the summary of this instance.
*/
diff --git a/src/main/java/com/authlete/common/dto/Service.java b/src/main/java/com/authlete/common/dto/Service.java
index 3ffdf39496..5837ec016a 100644
--- a/src/main/java/com/authlete/common/dto/Service.java
+++ b/src/main/java/com/authlete/common/dto/Service.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014-2025 Authlete, Inc.
+ * Copyright (C) 2014-2026 Authlete, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -330,7 +330,7 @@
*/
public class Service implements Serializable
{
- private static final long serialVersionUID = 83L;
+ private static final long serialVersionUID = 93L;
/*
@@ -738,6 +738,25 @@ public class Service implements Serializable
private long idTokenDuration;
+ /**
+ * Duration of authorization code in seconds.
+ *
+ * + * A value of {@code 0} means that this property is not set and that the + * implementation falls back to the default configuration on the server. + *
+ * + *+ * For FAPI 2.0, the lifetime of authorization codes is capped to 60 + * seconds. See OpenID FAPI 2.0 Security Profile for details. + *
+ * + * @since 4.40 + * @since Authlete 3.0.29 + */ + private long authorizationCodeDuration; + + /** * Duration of authorization response JWTs. * @@ -1883,6 +1902,207 @@ public class Service implements Serializable private boolean nativeSsoSupported; + /** + * The version of the OpenID for Verifiable Credential Issuance specification + * to support. + * + * @since 4.25 + * @since Authlete 3.0.22 + */ + private String oid4vciVersion; + + + /** + * Whether to support OAuth Client ID Metadata Document. + * + * @since 4.29 + * @since Authlete 3.0.22 + */ + private boolean clientIdMetadataDocumentSupported; + + + /** + * Whether to enable the allowlist for client IDs in the CIMD context. + * + * @since 4.31 + * @since Authlete 3.0.22 + */ + private boolean cimdAllowlistEnabled; + + + /** + * The allowlist for client IDs in the CIMD context. + * + * @since 4.31 + * @since Authlete 3.0.22 + */ + private String[] cimdAllowlist; + + + /** + * Whether to always retrieve client metadata in the CIMD context + * regardless of the cache's validity. + * + * @since 4.30 + * @since Authlete 3.0.22 + */ + private boolean cimdAlwaysRetrieved; + + + /** + * Whether to allow the {@code http} scheme in client IDs in the CIMD + * context. + * + * @since 4.30 + * @since Authlete 3.0.22 + */ + private boolean cimdHttpPermitted; + + + /** + * Whether to allow a query component in client IDs in the CIMD context. + * + * @since 4.30 + * @since Authlete 3.0.22 + */ + private boolean cimdQueryPermitted; + + + /** + * Whether to apply the metadata policy to client metadata that is obtained + * through the CIMD mechanism. + * + * @since 4.33 + * @since Authlete 3.0.23 + */ + private boolean cimdMetadataPolicyEnabled; + + + /** + * The metadata policy applied to client metadata that is obtained through + * the CIMD mechanism. + * + * @since 4.33 + * @since Authlete 3.0.23 + */ + private String cimdMetadataPolicy; + + + /** + * Whether to prohibit client ID aliases that start with {@code https://} + * or {@code http://}. + * + * @since 4.32 + * @since Authlete 3.0.22 + */ + private boolean httpAliasProhibited; + + + /** + * The challenge endpoint defined in + * OAuth 2.0 Attestation-Based Client Authentication. + * + * @since 4.39 + * @since Authlete 3.0.28 + */ + private URI challengeEndpoint; + + + /** + * The time window of attestation challenges in seconds. + * + * @since 4.39 + * @since Authlete 3.0.28 + */ + private long attestationChallengeTimeWindow; + + + /** + * The HAIP version to comply with. + * + * @since 4.41 + * @since Authlete 3.0.29 + */ + private String haipVersion; + + + /** + * The trusted root certificates for Client Attestation JWT. + * + * @since 4.42 + * @since Authlete 3.0.31 + */ + private String[] clientAttesterRoots; + + + /** + * Whether to enable the trusted root certificates for Client Attestation JWT. + * + * @since 4.42 + * @since Authlete 3.0.31 + */ + private boolean clientAttesterRootsEnabled; + + + /** + * Whether to refer to only the trusted root certificates for Client + * Attestation JWT, without referring to system-installed ones. + * + * @since 4.42 + * @since Authlete 3.0.31 + */ + private boolean clientAttesterRootsOnly; + + + /** + * The trusted root certificates for Key Attestation JWT. + * + * @since 4.42 + * @since Authlete 3.0.31 + */ + private String[] keyAttesterRoots; + + + /** + * Whether to enable the trusted root certificates for Key Attestation JWT. + * + * @since 4.42 + * @since Authlete 3.0.31 + */ + private boolean keyAttesterRootsEnabled; + + + /** + * Whether to refer to only the trusted root certificates for Key + * Attestation JWT, without referring to system-installed ones. + * + * @since 4.42 + * @since Authlete 3.0.31 + */ + private boolean keyAttesterRootsOnly; + + + /** + * Whether to support the OpenID Connect Back-Channel Logout 1.0 + * specification. + * + * @since 4.44 + * @since Authlete 3.0.32 + */ + private boolean backchannelLogoutSupported; + + + /** + * Whether to support the {@code sid} claim in Logout Tokens. + * + * @since 4.44 + * @since Authlete 3.0.32 + */ + private boolean backchannelLogoutSessionSupported; + + /** * Get the service number. * @@ -2971,6 +3191,39 @@ public Service setIdTokenDuration(long duration) } + /** + * Get the duration of authorization code in seconds. + * + * @return + * The duration of authorization code in seconds. + * + * @since 3.0.29 + */ + public long getAuthorizationCodeDuration() + { + return authorizationCodeDuration; + } + + + /** + * Set the duration of authorization code in seconds. + * + * @param duration + * The duration of authorization code in seconds. + * + * @return + * {@code this} object. + * + * @since 3.0.29 + */ + public Service setAuthorizationCodeDuration(long duration) + { + this.authorizationCodeDuration = duration; + + return this; + } + + /** * Get the duration of authorization response JWTs. * @@ -4234,6 +4487,8 @@ public Service setErrorUriOmitted(boolean omitted) * {@code false} if the feature is disabled. * * @since 2.2 + * + * @see #isHttpAliasProhibited() */ public boolean isClientIdAliasEnabled() { @@ -4260,6 +4515,8 @@ public boolean isClientIdAliasEnabled() * {@code this} object. * * @since 2.2 + * + * @see #isHttpAliasProhibited() */ public Service setClientIdAliasEnabled(boolean enabled) { @@ -11092,13 +11349,37 @@ public Service setPreAuthorizedGrantAnonymousAccessSupported(boolean supported) /** - * Get the duration of {@code c_nonce} in seconds. + * Get the duration (in 1.0-ID1) or validity period (time window) + * (in 1.0-Final) of {@code c_nonce} in seconds. + * + *+ * The method of generating {@code c_nonce} differs between OID4VCI 1.0-ID1 + * and OID4VCI 1.0-Final. + *
* *- * {@code c_nonce} is issued from the token endpoint of an authorization - * server in the pre-authorized code flow, and from the credential endpoint - * and the batch credential endpoint of a credential issuer. This property - * is used as the lifetime of the {@code c_nonce}. + * In 1.0-ID1, the {@code c_nonce} is issued from the token endpoint or the + * credential endpoint. In Authlete's implementation, the {@code c_nonce} + * for 1.0-ID1 is associated with an access token. + *
+ * + *+ * For {@code c_nonce} in 1.0-ID1, this {@code cnonceDuration} property is + * used as the lifetime of {@code c_nonce}. + *
+ * + *+ * In contrast, in 1.0-Final, the {@code c_nonce} is issued from the nonce + * endpoint. Requests to this endpoint do not include an access token, so + * the server cannot determine who the requester is. As a result, the + * {@code c_nonce} cannot be generated per access token or per client + * application. Consequently, the same {@code c_nonce} value is returned to + * anyone who accesses the endpoint within the same time window. + *
+ * + *+ * For {@code c_nonce} in 1.0-Final, this {@code cnonceDuration} property + * specifies the validity period (time window) in seconds. *
* *@@ -11114,12 +11395,14 @@ public Service setPreAuthorizedGrantAnonymousAccessSupported(boolean supported) *
* *+ * NOTE: * The {@code getCNonceDuration()} method added by the version 3.63 has * been renamed to {@code getCnonceDuration()} by the version 3.90. *
* * @return - * The duration of {@code c_nonce} in seconds. + * The duration of {@code c_nonce} in seconds in OID4VCI 1.0-ID1, or + * the validity period (time window) in seconds in OID4VCI 1.0-Final. * * @since 3.90 * @since Authlete 3.0 @@ -11134,13 +11417,37 @@ public long getCnonceDuration() /** - * Set the duration of {@code c_nonce} in seconds. + * Set the duration (in 1.0-ID1) or validity period (time window) + * (in 1.0-Final) of {@code c_nonce} in seconds. + * + *+ * The method of generating {@code c_nonce} differs between OID4VCI 1.0-ID1 + * and OID4VCI 1.0-Final. + *
+ * + *+ * In 1.0-ID1, the {@code c_nonce} is issued from the token endpoint or the + * credential endpoint. In Authlete's implementation, the {@code c_nonce} + * for 1.0-ID1 is associated with an access token. + *
+ * + *+ * For {@code c_nonce} in 1.0-ID1, this {@code cnonceDuration} property is + * used as the lifetime of {@code c_nonce}. + *
+ * + *+ * In contrast, in 1.0-Final, the {@code c_nonce} is issued from the nonce + * endpoint. Requests to this endpoint do not include an access token, so + * the server cannot determine who the requester is. As a result, the + * {@code c_nonce} cannot be generated per access token or per client + * application. Consequently, the same {@code c_nonce} value is returned to + * anyone who accesses the endpoint within the same time window. + *
* *- * {@code c_nonce} is issued from the token endpoint of an authorization - * server in the pre-authorized code flow, and from the credential endpoint - * and the batch credential endpoint of a credential issuer. This property - * is used as the lifetime of the {@code c_nonce}. + * For {@code c_nonce} in 1.0-Final, this {@code cnonceDuration} property + * specifies the validity period (time window) in seconds. *
* *@@ -11156,12 +11463,14 @@ public long getCnonceDuration() *
* *+ * NOTE: * The {@code setCNonceDuration(long)} method added by the version 3.63 has * been renamed to {@code setCnonceDuration(long)} by the version 3.90. *
* * @param duration - * The duration of {@code c_nonce} in seconds. + * The duration of {@code c_nonce} in seconds in OID4VCI 1.0-ID1, or + * the validity period (time window) in seconds in OID4VCI 1.0-Final. * * @return * {@code this} object. @@ -11962,4 +12271,1698 @@ public Service setNativeSsoSupported(boolean supported) return this; } + + + /** + * Get the version of the OpenID for Verifiable Credential Issuance + * specification to support. + * + *+ * Valid values are as follows: + *
+ * + *+ *+ * + * @return + * The version of the OpenID for Verifiable Credential Issuance + * specification to support. + * + * @since 4.25 + * @since Authlete 3.0.22 + * + * @see + * OpenID for Verifiable Credential Issuance + */ + public String getOid4vciVersion() + { + return oid4vciVersion; + } + + + /** + * Set the version of the OpenID for Verifiable Credential Issuance + * specification to support. + * + *+ *
+ *+ * + *Value + *Specification + *+ * + *null + *+ * + * OpenID for Verifiable Credential Issuance 1.0, Implementer's Draft 1 + * + *+ * + *{@code "1.0-ID1"} + *+ * + *{@code "1.0"} + *+ * + * OpenID for Verifiable Credential Issuance 1.0, Final + * + *+ * + *{@code "1.0-Final"} + *
+ * Valid values are as follows: + *
+ * + *+ *+ * + * @param version + * The version of the OpenID for Verifiable Credential Issuance + * specification to support. + * + * @return + * {@code this} object. + * + * @since 4.25 + * @since Authlete 3.0.22 + * + * @see + * OpenID for Verifiable Credential Issuance + */ + public Service setOid4vciVersion(String version) + { + this.oid4vciVersion = version; + + return this; + } + + + /** + * Get the flag to determine to support OAuth Client ID Metadata Document. + * + *+ *
+ *+ * + *Value + *Specification + *+ * + *null + *+ * + * OpenID for Verifiable Credential Issuance 1.0, Implementer's Draft 1 + * + *+ * + *{@code "1.0-ID1"} + *+ * + *{@code "1.0"} + *+ * + * OpenID for Verifiable Credential Issuance 1.0, Final + * + *+ * + *{@code "1.0-Final"} + *
+ * This flag corresponds to the {@code client_id_metadata_document_supported} + * metadata parameter. + *
+ * + * @return + * {@code true} if OAuth Client ID Metadata Document is supported. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public boolean isClientIdMetadataDocumentSupported() + { + return clientIdMetadataDocumentSupported; + } + + + /** + * Set the flag to determine to support OAuth Client ID Metadata Document. + * + *+ * This flag corresponds to the {@code client_id_metadata_document_supported} + * metadata parameter. + *
+ * + * @param supported + * {@code true} to support OAuth Client ID Metadata Document. + * + * @return + * {@code this} object. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public Service setClientIdMetadataDocumentSupported(boolean supported) + { + this.clientIdMetadataDocumentSupported = supported; + + return this; + } + + + /** + * Get the flag that indicates whether the allowlist for client IDs + * in the CIMD context is enabled or not. + * + *+ * If the allowlist is enabled, the client ID in a request must match at + * least one entry in the allowlist to be considered a valid client ID + * in the CIMD context. + *
+ * + * @return + * {@code true} if the allowlist for client IDs in the CIMD + * context is enabled. + * + * @since 4.31 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public boolean isCimdAllowlistEnabled() + { + return cimdAllowlistEnabled; + } + + + /** + * Set the flag that indicates whether the allowlist for client IDs + * in the CIMD context is enabled or not. + * + *+ * If the allowlist is enabled, the client ID in a request must match at + * least one entry in the allowlist to be considered a valid client ID + * in the CIMD context. + *
+ * + * @param enabled + * {@code true} to enable the allowlist for client IDs in the + * CIMD context. + * + * @return + * {@code this} object. + * + * @since 4.31 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public Service setCimdAllowlistEnabled(boolean enabled) + { + this.cimdAllowlistEnabled = enabled; + + return this; + } + + + /** + * Get the allowlist for client IDs in the CIMD context. + * + *+ * When the allowlist feature is enabled (see {@link #isCimdAllowlistEnabled()}), + * the allowlist specified by this property is consulted to determine + * whether the client ID in a request is valid in the CIMD context. + * In this case, the client ID must match at least one entry in the + * allowlist. + *
+ * + *+ * Each entry in the allowlist must be a valid URI. The comparison between + * an allowlist entry and a client ID is performed as follows: + *
+ * + *+ * For example, if the allowlist contains "{@code https://example.com/a/b}", + * then "{@code https://example.com/a/b/c}" is considered valid, but + * "{@code https://example.com/a}" is not. + *
+ * + * @return + * The allowlist for client IDs in the CIMD context. + * + * @since 4.31 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public String[] getCimdAllowlist() + { + return cimdAllowlist; + } + + + /** + * Set the allowlist for client IDs in the CIMD context. + * + *+ * When the allowlist feature is enabled (see {@link #isCimdAllowlistEnabled()}), + * the allowlist specified by this property is consulted to determine + * whether the client ID in a request is valid in the CIMD context. + * In this case, the client ID must match at least one entry in the + * allowlist. + *
+ * + *+ * Each entry in the allowlist must be a valid URI. The comparison between + * an allowlist entry and a client ID is performed as follows: + *
+ * + *+ * For example, if the allowlist contains "{@code https://example.com/a/b}", + * then "{@code https://example.com/a/b/c}" is considered valid, but + * "{@code https://example.com/a}" is not. + *
+ * + * @param allowlist + * The allowlist for client IDs in the CIMD context. + * + * @return + * {@code this} object. + * + * @since 4.31 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public Service setCimdAllowlist(String[] allowlist) + { + this.cimdAllowlist = allowlist; + + return this; + } + + + /** + * Get the flag that indicates whether Authlete should always fetch the + * client metadata from the location specified by the client ID (when + * CIMD applies), regardless of whether a cached copy already exists + * and has not yet expired. + * + *+ * Under normal circumstances, client metadata retrieved from the location + * referenced by the client ID is stored in the database with an expiration + * time calculated using HTTP caching mechanisms (see RFC 9111 HTTP Caching). + * Until that expiration time is reached, Authlete does not attempt to + * retrieve the client metadata again. + *
+ * + *+ * When this flag is set to {@code true}, Authlete retrieves the client + * metadata regardless of the cache's validity. + *
+ * + *+ * Some Authlete APIs accept a {@code cimdOptions.alwaysRetrieved} request + * parameter (see {@link CimdOptions}). If the parameter is provided and + * its value is {@code true}, it takes precedence over this service + * configuration. + *
+ * + *+ * This flag is effective only when the service supports CIMD (see {@link + * Service#isClientIdMetadataDocumentSupported()}) and CIMD is actually + * used to resolve client metadata. For example, if the client ID in a + * request does not appear to be a valid URI, CIMD will not be used even + * if the service is configured to support it. In such cases, this flag + * has no effect. + *
+ * + *+ * Client metadata retrieval is performed only in the initiating request + * of an authorization flow, and not in any subsequent requests. For + * example, in the authorization code flow, metadata may be retrieved + * during the authorization request, but not during the subsequent token + * request. In contrast, in the client credentials flow, metadata retrieval + * may occur because the token request itself is the initiating request + * in the flow. + *
+ * + * @return + * {@code true} if Authlete attempts to retrieve client metadata + * regardless of the cache's validity. + * + * @since 4.30 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public boolean isCimdAlwaysRetrieved() + { + return cimdAlwaysRetrieved; + } + + + /** + * Set the flag that indicates whether Authlete should always fetch the + * client metadata from the location specified by the client ID (when + * CIMD applies), regardless of whether a cached copy already exists + * and has not yet expired. + * + *+ * Under normal circumstances, client metadata retrieved from the location + * referenced by the client ID is stored in the database with an expiration + * time calculated using HTTP caching mechanisms (see RFC 9111 HTTP Caching). + * Until that expiration time is reached, Authlete does not attempt to + * retrieve the client metadata again. + *
+ * + *+ * When this flag is set to {@code true}, Authlete retrieves the client + * metadata regardless of the cache's validity. + *
+ * + *+ * Some Authlete APIs accept a {@code cimdOptions.alwaysRetrieved} request + * parameter (see {@link CimdOptions}). If the parameter is provided and + * its value is {@code true}, it takes precedence over this service + * configuration. + *
+ * + *+ * This flag is effective only when the service supports CIMD (see {@link + * Service#isClientIdMetadataDocumentSupported()}) and CIMD is actually + * used to resolve client metadata. For example, if the client ID in a + * request does not appear to be a valid URI, CIMD will not be used even + * if the service is configured to support it. In such cases, this flag + * has no effect. + *
+ * + *+ * Client metadata retrieval is performed only in the initiating request + * of an authorization flow, and not in any subsequent requests. For + * example, in the authorization code flow, metadata may be retrieved + * during the authorization request, but not during the subsequent token + * request. In contrast, in the client credentials flow, metadata retrieval + * may occur because the token request itself is the initiating request + * in the flow. + *
+ * + * @param always + * {@code true} to instruct Authlete to retrieve client metadata + * regardless of the cache's validity. + * + * @return + * {@code this} object. + * + * @since 4.30 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public Service setCimdAlwaysRetrieved(boolean always) + { + this.cimdAlwaysRetrieved = always; + + return this; + } + + + /** + * Get the flag that indicates whether the {@code http} scheme in the client + * ID is permitted (when CIMD applies). + * + *+ * The specification requires the {@code https} scheme, but if this flag is + * set to {@code true}, Authlete also allows the {@code http} scheme. The + * main purpose of this option is to make development easier for developers + * who run CIMD-enabled servers and a web server publishing client metadata + * on their local machines without TLS. + *
+ * + *+ * Given this purpose, it is not recommended to enable this option in + * production environments unless an allowlist is used (see {@link + * Service#isCimdAllowlistEnabled()}). + *
+ * + *+ * Some Authlete APIs accept a {@code cimdOptions.httpPermitted} request + * parameter (see {@link CimdOptions}). If the parameter is provided and + * its value is {@code true}, it takes precedence over this service + * configuration. + *
+ * + * @return + * {@code true} if the {@code http} scheme in the client ID is + * permitted. + * + * @since 4.30 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public boolean isCimdHttpPermitted() + { + return cimdHttpPermitted; + } + + + /** + * Set the flag that indicates whether the {@code http} scheme in the client + * ID is permitted (when CIMD applies). + * + *+ * The specification requires the {@code https} scheme, but if this flag is + * set to {@code true}, Authlete also allows the {@code http} scheme. The + * main purpose of this option is to make development easier for developers + * who run CIMD-enabled servers and a web server publishing client metadata + * on their local machines without TLS. + *
+ * + *+ * Given this purpose, it is not recommended to enable this option in + * production environments unless an allowlist is used (see {@link + * Service#isCimdAllowlistEnabled()}). + *
+ * + *+ * Some Authlete APIs accept a {@code cimdOptions.httpPermitted} request + * parameter (see {@link CimdOptions}). If the parameter is provided and + * its value is {@code true}, it takes precedence over this service + * configuration. + *
+ * + * @param permitted + * {@code true} to permit the {@code http} scheme in the client ID. + * + * @return + * {@code this} object. + * + * @since 4.30 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public Service setCimdHttpPermitted(boolean permitted) + { + this.cimdHttpPermitted = permitted; + + return this; + } + + + /** + * Get the flag that indicates whether a query component in the client ID + * is permitted (when CIMD applies). + * + *+ * Although the specification states that a client ID "SHOULD NOT + * include a query string component," it does technically allow it. + * However, query components are prone to misuse. Therefore, Authlete does + * not allow them by default. Setting this flag to {@code true} relaxes + * that restriction. + *
+ * + *+ * Some Authlete APIs accept a {@code cimdOptions.queryPermitted} request + * parameter (see {@link CimdOptions}). If the parameter is provided and + * its value is {@code true}, it takes precedence over this service + * configuration. + *
+ * + * @return + * {@code true} if a query component in the client ID is permitted. + * + * @since 4.30 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public boolean isCimdQueryPermitted() + { + return cimdQueryPermitted; + } + + + /** + * Set the flag that indicates whether a query component in the client ID + * is permitted (when CIMD applies). + * + *+ * Although the specification states that a client ID "SHOULD NOT + * include a query string component," it does technically allow it. + * However, query components are prone to misuse. Therefore, Authlete does + * not allow them by default. Setting this flag to {@code true} relaxes + * that restriction. + *
+ * + *+ * Some Authlete APIs accept a {@code cimdOptions.queryPermitted} request + * parameter (see {@link CimdOptions}). If the parameter is provided and + * its value is {@code true}, it takes precedence over this service + * configuration. + *
+ * + * @param permitted + * {@code true} to permit a query component in the client ID. + * + * @return + * {@code this} object. + * + * @since 4.30 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public Service setCimdQueryPermitted(boolean permitted) + { + this.cimdQueryPermitted = permitted; + + return this; + } + + + /** + * Get the flag that indicates whether to apply the metadata policy to + * client metadata that is obtained through the CIMD mechanism. + * + *+ * If this flag is set to {@code true}, the metadata policy specified by + * the {@code cimdMetadataPolicy} property, if available, is applied to + * the client metadata that is obtained through the CIMD mechanism. + *
+ * + * @return + * {@code true} if the metadata policy is applied to client + * metadata that is obtained through the CIMD mechanism. + * + * @since 4.33 + * @since Authlete 3.0.23 + * + * @see + * OAuth Client ID Metadata Document + */ + public boolean isCimdMetadataPolicyEnabled() + { + return cimdMetadataPolicyEnabled; + } + + + /** + * Set the flag that indicates whether to apply the metadata policy to + * client metadata that is obtained through the CIMD mechanism. + * + *+ * If this flag is set to {@code true}, the metadata policy specified by + * the {@code cimdMetadataPolicy} property, if available, is applied to + * the client metadata that is obtained through the CIMD mechanism. + *
+ * + * @param enabled + * {@code true} to apply the metadata policy to client metadata + * that is obtained through the CIMD mechanism. + * + * @return + * {@code this} object. + * + * @since 4.33 + * @since Authlete 3.0.23 + * + * @see + * OAuth Client ID Metadata Document + */ + public Service setCimdMetadataPolicyEnabled(boolean enabled) + { + this.cimdMetadataPolicyEnabled = enabled; + + return this; + } + + + /** + * Get the metadata policy applied to client metadata that is obtained + * through the CIMD mechanism. + * + *+ * If the {@code cimdMetadataPolicyEnabled} property is set to {@code true}, + * the metadata policy specified by this {@code cimdMetadataPolicy} property + * is applied to client metadata that is obtained through the CIMD mechanism. + *
+ * + *+ * The metadata policy must comply with the grammar defined in 6.1. Metadata Policy of the OpenID Federation + * 1.0 specification. Below is an example of metadata policy: + *
+ * + *
+ * {
+ * "grant_types": {
+ * "default": [
+ * "authorization_code"
+ * ],
+ * "subset_of": [
+ * "authorization_code",
+ * "refresh_token"
+ * ],
+ * "superset_of": [
+ * "authorization_code"
+ * ]
+ * },
+ * "token_endpoint_auth_method": {
+ * "one_of": [
+ * "private_key_jwt",
+ * "self_signed_tls_client_auth"
+ * ],
+ * "essential": true
+ * },
+ * "token_endpoint_auth_signing_alg" : {
+ * "one_of": [
+ * "PS256",
+ * "ES256"
+ * ]
+ * }
+ * }
+ *
+ * @return
+ * The metadata policy applied to client metadata that is obtained
+ * through the CIMD mechanism.
+ *
+ * @since 4.33
+ * @since Authlete 3.0.23
+ *
+ * @see
+ * OAuth Client ID Metadata Document
+ * @see
+ * OpenID Federation 1.0, Section 6.1. Metadata Policy
+ */
+ public String getCimdMetadataPolicy()
+ {
+ return cimdMetadataPolicy;
+ }
+
+
+ /**
+ * Set the metadata policy applied to client metadata that is obtained
+ * through the CIMD mechanism.
+ *
+ * + * If the {@code cimdMetadataPolicyEnabled} property is set to {@code true}, + * the metadata policy specified by this {@code cimdMetadataPolicy} property + * is applied to client metadata that is obtained through the CIMD mechanism. + *
+ * + *+ * The metadata policy must comply with the grammar defined in 6.1. Metadata Policy of the OpenID Federation + * 1.0 specification. Below is an example of metadata policy: + *
+ * + *
+ * {
+ * "grant_types": {
+ * "default": [
+ * "authorization_code"
+ * ],
+ * "subset_of": [
+ * "authorization_code",
+ * "refresh_token"
+ * ],
+ * "superset_of": [
+ * "authorization_code"
+ * ]
+ * },
+ * "token_endpoint_auth_method": {
+ * "one_of": [
+ * "private_key_jwt",
+ * "self_signed_tls_client_auth"
+ * ],
+ * "essential": true
+ * },
+ * "token_endpoint_auth_signing_alg" : {
+ * "one_of": [
+ * "PS256",
+ * "ES256"
+ * ]
+ * }
+ * }
+ *
+ * @param policy
+ * The metadata policy applied to client metadata that is obtained
+ * through the CIMD mechanism.
+ *
+ * @return
+ * {@code this} object.
+ *
+ * @since 4.33
+ * @since Authlete 3.0.23
+ *
+ * @see
+ * OAuth Client ID Metadata Document
+ * @see
+ * OpenID Federation 1.0, Section 6.1. Metadata Policy
+ */
+ public Service setCimdMetadataPolicy(String policy)
+ {
+ this.cimdMetadataPolicy = policy;
+
+ return this;
+ }
+
+
+ /**
+ * Get the flag that indicates whether to prohibit client ID aliases that
+ * start with {@code https://} or {@code http://}.
+ *
+ * + * The primary purpose of this flag is to prevent the use of client ID + * aliases that may conflict with entity IDs in OpenID Federation + * 1.0 or metadata document locations in CIMD. + *
+ * + *+ * For backward compatibility, the default value of this flag is set to + * {@code false}, but it is recommended to set it to {@code true} whenever + * possible. + *
+ * + * @return + * {@code true} if client ID aliases that start with {@code https://} + * or {@code http://} are prohibited. + * + * @since 4.32 + * @since Authlete 3.0.22 + * + * @see + * OpenID Federation 1.0 + * @see + * OAuth Client ID Metadata Document + */ + public boolean isHttpAliasProhibited() + { + return httpAliasProhibited; + } + + + /** + * Set the flag that indicates whether to prohibit client ID aliases that + * start with {@code https://} or {@code http://}. + * + *+ * The primary purpose of this flag is to prevent the use of client ID + * aliases that may conflict with entity IDs in OpenID Federation + * 1.0 or metadata document locations in CIMD. + *
+ * + *+ * For backward compatibility, the default value of this flag is set to + * {@code false}, but it is recommended to set it to {@code true} whenever + * possible. + *
+ * + * @param prohibited + * {@code true} to prohibit client ID aliases that start with + * {@code https://} or {@code http://}. + * + * @return + * {@code this} object. + * + * @since 4.32 + * @since Authlete 3.0.22 + * + * @see + * OpenID Federation 1.0 + * @see + * OAuth Client ID Metadata Document + */ + public Service setHttpAliasProhibited(boolean prohibited) + { + this.httpAliasProhibited = prohibited; + + return this; + } + + + /** + * Get the URI of the challenge endpoint that is defined in + * OAuth 2.0 Attestation-Based Client Authentication. + * + * @return + * The URI of the challenge endpoint. + * + * @since 4.39 + * @since Authlete 3.0.28 + * + * @see + * OAuth 2.0 Attestation-Based Client Authentication + */ + public URI getChallengeEndpoint() + { + return challengeEndpoint; + } + + + /** + * Set the URI of the challenge endpoint that is defined in + * OAuth 2.0 Attestation-Based Client Authentication. + * + * @param endpoint + * The URI of the challenge endpoint. + * + * @return + * {@code this} object. + * + * @since 4.39 + * @since Authlete 3.0.28 + * + * @see + * OAuth 2.0 Attestation-Based Client Authentication + */ + public Service setChallengeEndpoint(URI endpoint) + { + this.challengeEndpoint = endpoint; + + return this; + } + + + /** + * Get the time window of attestation challenges in seconds. + * + *+ * The value of this property is used as the time window of attestation + * challenges issued from the challenge endpoint defined in the + * OAuth 2.0 Attestation-Based Client Authentication. + *
+ * + * @return + * The time window of attestation challenges in seconds. + * + * @since 4.39 + * @since Authlete 3.0.28 + * + * @see + * OAuth 2.0 Attestation-Based Client Authentication + */ + public long getAttestationChallengeTimeWindow() + { + return attestationChallengeTimeWindow; + } + + + /** + * Set the time window of attestation challenges in seconds. + * + *+ * The value of this property is used as the time window of attestation + * challenges issued from the challenge endpoint defined in the + * OAuth 2.0 Attestation-Based Client Authentication. + *
+ * + * @param timeWindow + * The time window of attestation challenges in seconds. + * + * @return + * {@code this} object. + * + * @since 4.39 + * @since Authlete 3.0.28 + * + * @see + * OAuth 2.0 Attestation-Based Client Authentication + */ + public Service setAttestationChallengeTimeWindow(long timeWindow) + { + this.attestationChallengeTimeWindow = timeWindow; + + return this; + } + + + /** + * Get the HAIP version to comply with. Valid values for this property are + * as follows: + * + *+ * If this property is set, requests to this service undergo additional + * validation based on the specified version of the HAIP specification. + *
+ * + *+ * Note that even if this property is not set, additional validation based + * on the HAIP specification is performed if the {@link Client}'s + * {@code haipVersion} is set or if a scope with the {@code haip} attribute + * is included in the request. + *
+ * + * @return + * The HAIP version to comply with. + * + * @since 4.41 + * @since 3.0.29 + * + * @see + * OpenID4VC High Assurance Interoperability Profile 1.0 + */ + public String getHaipVersion() + { + return haipVersion; + } + + + /** + * Set the HAIP version to comply with. Valid values for this property are + * as follows: + * + *+ * If this property is set, requests to this service undergo additional + * validation based on the specified version of the HAIP specification. + *
+ * + *+ * Note that even if this property is not set, additional validation based + * on the HAIP specification is performed if the {@link Client}'s + * {@code haipVersion} is set or if a scope with the {@code haip} attribute + * is included in the request. + *
+ * + * @param version + * The HAIP version to comply with. + * + * @return + * {@code this} object. + * + * @since 4.41 + * @since 3.0.29 + * + * @see + * OpenID4VC High Assurance Interoperability Profile 1.0 + */ + public Service setHaipVersion(String version) + { + this.haipVersion = version; + + return this; + } + + + /** + * Get the trusted root certificates used to validate the certificate chain + * specified in the {@code x5c} header parameter of the Client Attestation + * JWT. + * + *+ * If the Client Attestation JWT includes the {@code x5c} header parameter, + * Authlete validates the specified certificate chain. During validation, + * Authlete uses the X.509 certificates specified by this property as + * trusted root certificates when the {@code clientAttesterRootsEnabled} + * property is set to {@code true}. System-installed root certificates are + * also used unless the {@code clientAttesterRootsOnly} property is set to + * {@code true}. + *
+ * + *+ * Each element in this property represents an X.509 certificate. The value + * is a base64-encoded string of the certificate in DER format, with or + * without PEM markers. The base64-encoded string may contain newlines and + * other whitespace characters. + *
+ * + *+ * When Authlete returns this certificate list from its APIs, the certificates + * are provided in PEM format with PEM markers. + *
+ * + *+ * Note that the OpenID4VC High Assurance Interoperability Profile 1.0 specification + * requires the Client Attestation JWT to include the {@code x5c} header + * parameter. + *
+ * + * @return + * Trusted root certificates used to validate the certificate chain + * of the Client Attestation JWT. + * + * @see + * OAuth 2.0 Attestation-Based Client Authentication + * @see + * OpenID4VC High Assurance Interoperability Profile 1.0 + * @see #isClientAttesterRootsEnabled() + * @see #isClientAttesterRootsOnly() + * + * @since 4.42 + * @since Authlete 3.0.31 + */ + public String[] getClientAttesterRoots() + { + return clientAttesterRoots; + } + + + /** + * Set the trusted root certificates used to validate the certificate chain + * specified in the {@code x5c} header parameter of the Client Attestation + * JWT. + * + *+ * If the Client Attestation JWT includes the {@code x5c} header parameter, + * Authlete validates the specified certificate chain. During validation, + * Authlete uses the X.509 certificates specified by this property as + * trusted root certificates when the {@code clientAttesterRootsEnabled} + * property is set to {@code true}. System-installed root certificates are + * also used unless the {@code clientAttesterRootsOnly} property is set to + * {@code true}. + *
+ * + *+ * Each element in this property represents an X.509 certificate. The value + * is a base64-encoded string of the certificate in DER format, with or + * without PEM markers. The base64-encoded string may contain newlines and + * other whitespace characters. + *
+ * + *+ * When Authlete returns this certificate list from its APIs, the certificates + * are provided in PEM format with PEM markers. + *
+ * + *+ * Note that the OpenID4VC High Assurance Interoperability Profile 1.0 specification + * requires the Client Attestation JWT to include the {@code x5c} header + * parameter. + *
+ * + * @param certificates + * Trusted root certificates used to validate the certificate chain + * of the Client Attestation JWT. + * + * @return + * {@code this} object. + * + * @see + * OAuth 2.0 Attestation-Based Client Authentication + * @see + * OpenID4VC High Assurance Interoperability Profile 1.0 + * @see #setClientAttesterRootsEnabled(boolean) + * @see #setClientAttesterRootsOnly(boolean) + * + * @since 4.42 + * @since Authlete 3.0.31 + */ + public Service setClientAttesterRoots(String[] certificates) + { + this.clientAttesterRoots = certificates; + + return this; + } + + + /** + * Get the flag indicating whether to use the trusted root certificates + * specified by the {@code clientAttesterRoots} property to validate the + * certificate chain specified in the {@code x5c} header parameter of the + * Client Attestation JWT. + * + * @return + * {@code true} if the trusted root certificates specified by the + * {@code clientAttesterRoots} property are used to validate the + * certificate chain of the Client Attestation JWT. + * + * @see + * OAuth 2.0 Attestation-Based Client Authentication + * @see + * OpenID4VC High Assurance Interoperability Profile 1.0 + * @see #getClientAttesterRoots() + * @see #isClientAttesterRootsOnly() + * + * @since 4.42 + * @since Authlete 3.0.31 + */ + public boolean isClientAttesterRootsEnabled() + { + return clientAttesterRootsEnabled; + } + + + /** + * Set the flag indicating whether to use the trusted root certificates + * specified by the {@code clientAttesterRoots} property to validate the + * certificate chain specified in the {@code x5c} header parameter of the + * Client Attestation JWT. + * + * @param enabled + * {@code true} to use the trusted root certificates specified by + * the {@code clientAttesterRoots} property. + * + * @return + * {@code this} object. + * + * @see + * OAuth 2.0 Attestation-Based Client Authentication + * @see + * OpenID4VC High Assurance Interoperability Profile 1.0 + * @see #setClientAttesterRoots(String[]) + * @see #setClientAttesterRootsOnly(boolean) + * + * @since 4.42 + * @since Authlete 3.0.31 + */ + public Service setClientAttesterRootsEnabled(boolean enabled) + { + this.clientAttesterRootsEnabled = enabled; + + return this; + } + + + /** + * Get the flag indicating whether to use only the trusted root certificates + * specified by the {@code clientAttesterRoots} property to validate the + * certificate chain specified in the {@code x5c} header parameter of the + * Client Attestation JWT, without using system-installed root certificates. + * + * @return + * {@code true} if only the trusted root certificates specified by + * the {@code clientAttesterRoots} property are used. + * + * @see + * OAuth 2.0 Attestation-Based Client Authentication + * @see + * OpenID4VC High Assurance Interoperability Profile 1.0 + * @see #getClientAttesterRoots() + * @see #isClientAttesterRootsEnabled() + * + * @since 4.42 + * @since Authlete 3.0.31 + */ + public boolean isClientAttesterRootsOnly() + { + return clientAttesterRootsOnly; + } + + + /** + * Set the flag indicating whether to use only the trusted root certificates + * specified by the {@code clientAttesterRoots} property to validate the + * certificate chain specified in the {@code x5c} header parameter of the + * Client Attestation JWT, without using system-installed root certificates. + * + * @param only + * {@code true} to use only the trusted root certificates specified + * by the {@code clientAttesterRoots} property. + * + * @return + * {@code this} object. + * + * @see + * OAuth 2.0 Attestation-Based Client Authentication + * @see + * OpenID4VC High Assurance Interoperability Profile 1.0 + * @see #setClientAttesterRoots(String[]) + * @see #isClientAttesterRootsEnabled() + * + * @since 4.42 + * @since Authlete 3.0.31 + */ + public Service setClientAttesterRootsOnly(boolean only) + { + this.clientAttesterRootsOnly = only; + + return this; + } + + + /** + * Get the trusted root certificates used to validate the certificate chain + * specified in the {@code x5c} header parameter of the Key Attestation JWT. + * + *+ * If the Key Attestation JWT includes the {@code x5c} header parameter, + * Authlete validates the specified certificate chain. During validation, + * Authlete uses the X.509 certificates specified by this property as + * trusted root certificates when the {@code keyAttesterRootsEnabled} + * property is set to {@code true}. System-installed root certificates are + * also used unless the {@code keyAttesterRootsOnly} property is set to + * {@code true}. + *
+ * + *+ * Each element in this property represents an X.509 certificate. The value + * is a base64-encoded string of the certificate in DER format, with or + * without PEM markers. The base64-encoded string may contain newlines and + * other whitespace characters. + *
+ * + *+ * When Authlete returns this certificate list from its APIs, the certificates + * are provided in PEM format with PEM markers. + *
+ * + *+ * Note that the OpenID4VC High Assurance Interoperability Profile 1.0 specification + * requires the Key Attestation JWT to include the {@code x5c} header + * parameter. + *
+ * + * @return + * Trusted root certificates used to validate the certificate chain + * of the Key Attestation JWT. + * + * @see + * OpenID for Verifiable Credential Issuance 1.0 + * @see + * OpenID4VC High Assurance Interoperability Profile 1.0 + * @see #isKeyAttesterRootsEnabled() + * @see #isKeyAttesterRootsOnly() + * + * @since 4.42 + * @since Authlete 3.0.31 + */ + public String[] getKeyAttesterRoots() + { + return keyAttesterRoots; + } + + + /** + * Set the trusted root certificates used to validate the certificate chain + * specified in the {@code x5c} header parameter of the Key Attestation JWT. + * + *+ * If the Key Attestation JWT includes the {@code x5c} header parameter, + * Authlete validates the specified certificate chain. During validation, + * Authlete uses the X.509 certificates specified by this property as + * trusted root certificates when the {@code keyAttesterRootsEnabled} + * property is set to {@code true}. System-installed root certificates are + * also used unless the {@code keyAttesterRootsOnly} property is set to + * {@code true}. + *
+ * + *+ * Each element in this property represents an X.509 certificate. The value + * is a base64-encoded string of the certificate in DER format, with or + * without PEM markers. The base64-encoded string may contain newlines and + * other whitespace characters. + *
+ * + *+ * When Authlete returns this certificate list from its APIs, the certificates + * are provided in PEM format with PEM markers. + *
+ * + *+ * Note that the OpenID4VC High Assurance Interoperability Profile 1.0 specification + * requires the Key Attestation JWT to include the {@code x5c} header + * parameter. + *
+ * + * @param certificates + * Trusted root certificates used to validate the certificate chain + * of the Key Attestation JWT. + * + * @return + * {@code this} object. + * + * @see + * OpenID for Verifiable Credential Issuance 1.0 + * @see + * OpenID4VC High Assurance Interoperability Profile 1.0 + * @see #setKeyAttesterRootsEnabled(boolean) + * @see #setKeyAttesterRootsOnly(boolean) + * + * @since 4.42 + * @since Authlete 3.0.31 + */ + public Service setKeyAttesterRoots(String[] certificates) + { + this.keyAttesterRoots = certificates; + + return this; + } + + + /** + * Get the flag indicating whether to use the trusted root certificates + * specified by the {@code keyAttesterRoots} property to validate the + * certificate chain specified in the {@code x5c} header parameter of the + * Key Attestation JWT. + * + * @return + * {@code true} if the trusted root certificates specified by the + * {@code keyAttesterRoots} property are used to validate the + * certificate chain of the Key Attestation JWT. + * + * @see + * OpenID for Verifiable Credential Issuance 1.0 + * @see + * OpenID4VC High Assurance Interoperability Profile 1.0 + * @see #getKeyAttesterRoots() + * @see #isKeyAttesterRootsOnly() + * + * @since 4.42 + * @since Authlete 3.0.31 + */ + public boolean isKeyAttesterRootsEnabled() + { + return keyAttesterRootsEnabled; + } + + + /** + * Set the flag indicating whether to use the trusted root certificates + * specified by the {@code keyAttesterRoots} property to validate the + * certificate chain specified in the {@code x5c} header parameter of the + * Key Attestation JWT. + * + * @param enabled + * {@code true} to use the trusted root certificates specified by + * the {@code keyAttesterRoots} property. + * + * @return + * {@code this} object. + * + * @see + * OpenID for Verifiable Credential Issuance 1.0 + * @see + * OpenID4VC High Assurance Interoperability Profile 1.0 + * @see #setKeyAttesterRoots(String[]) + * @see #setKeyAttesterRootsOnly(boolean) + * + * @since 4.42 + * @since Authlete 3.0.31 + */ + public Service setKeyAttesterRootsEnabled(boolean enabled) + { + this.keyAttesterRootsEnabled = enabled; + + return this; + } + + + /** + * Get the flag indicating whether to use only the trusted root certificates + * specified by the {@code keyAttesterRoots} property to validate the + * certificate chain specified in the {@code x5c} header parameter of the + * Key Attestation JWT, without using system-installed root certificates. + * + * @return + * {@code true} if only the trusted root certificates specified by + * the {@code keyAttesterRoots} property are used. + * + * @see + * OpenID for Verifiable Credential Issuance 1.0 + * @see + * OpenID4VC High Assurance Interoperability Profile 1.0 + * @see #getKeyAttesterRoots() + * @see #isKeyAttesterRootsEnabled() + * + * @since 4.42 + * @since Authlete 3.0.31 + */ + public boolean isKeyAttesterRootsOnly() + { + return keyAttesterRootsOnly; + } + + + /** + * Set the flag indicating whether to use only the trusted root certificates + * specified by the {@code keyAttesterRoots} property to validate the + * certificate chain specified in the {@code x5c} header parameter of the + * Key Attestation JWT, without using system-installed root certificates. + * + * @param only + * {@code true} to use only the trusted root certificates specified + * by the {@code keyAttesterRoots} property. + * + * @return + * {@code this} object. + * + * @see + * OpenID for Verifiable Credential Issuance 1.0 + * @see + * OpenID4VC High Assurance Interoperability Profile 1.0 + * @see #setKeyAttesterRoots(String[]) + * @see #isKeyAttesterRootsEnabled() + * + * @since 4.42 + * @since Authlete 3.0.31 + */ + public Service setKeyAttesterRootsOnly(boolean only) + { + this.keyAttesterRootsOnly = only; + + return this; + } + + + /** + * Get the flag indicating whether to support the OpenID + * Connect Back-Channel Logout 1.0 specification. This property + * corresponds to thebackchannel_logout_supported
+ * server metadata parameter defined in the specification.
+ *
+ *
+ * The /api
+ * API can be used to generate a Logout Token. See {@link BackchannelLogoutTokenRequest} and
+ * {@link BackchannelLogoutTokenResponse} for details.
+ *
backchannel_logout_supported
+ * server metadata parameter defined in the specification.
+ *
+ *
+ * The /api
+ * API can be used to generate a Logout Token. See {@link BackchannelLogoutTokenRequest} and
+ * {@link BackchannelLogoutTokenResponse} for details.
+ *
backchannel_logout_session_supported server
+ * metadata parameter defined in the OpenID
+ * Connect Back-Channel Logout 1.0 specification.
+ *
+ *
+ * If this property is set to {@code true}, the {@code sessionId} request
+ * parameter for the /auth API
+ * (see {@link AuthorizationIssueRequest#getSessionId()}) is recognized,
+ * and the specified value is embedded in ID Tokens as the value of the
+ * {@code sid} claim.
+ *
+ * If this property is set to {@code true}, API calls to the
+ * /api/{service-id}
+ * API should include the {@code sessionId} request parameter (see
+ * {@link BackchannelLogoutTokenRequest#getSessionId()}).
+ *
backchannel_logout_session_supported server
+ * metadata parameter defined in the OpenID
+ * Connect Back-Channel Logout 1.0 specification.
+ *
+ *
+ * If this property is set to {@code true}, the {@code sessionId} request
+ * parameter for the /auth API
+ * (see {@link AuthorizationIssueRequest#getSessionId()}) is recognized,
+ * and the specified value is embedded in ID Tokens as the value of the
+ * {@code sid} claim.
+ *
+ * If this property is set to {@code true}, API calls to the
+ * /api/{service-id}
+ * API should include the {@code sessionId} request parameter (see
+ * {@link BackchannelLogoutTokenRequest#getSessionId()}).
+ *
- * Note that {@code clientIdAliasUsed} and {@code clientEntityIdUsed} are - * mutually exclusive. + * Note that {@code clientIdAliasUsed}, {@code clientEntityIdUsed}, and + * {@code metadataDocumentUsed} are mutually exclusive. *
* * @return @@ -633,8 +634,8 @@ public boolean isClientIdAliasUsed() * * *- * Note that {@code clientIdAliasUsed} and {@code clientEntityIdUsed} are - * mutually exclusive. + * Note that {@code clientIdAliasUsed}, {@code clientEntityIdUsed}, and + * {@code metadataDocumentUsed} are mutually exclusive. *
* * @param used @@ -679,8 +680,8 @@ public TokenCreateRequest setClientIdAliasUsed(boolean used) * * *- * Note that {@code clientIdAliasUsed} and {@code clientEntityIdUsed} are - * mutually exclusive. + * Note that {@code clientIdAliasUsed}, {@code clientEntityIdUsed}, and + * {@code metadataDocumentUsed} are mutually exclusive. *
* * @return @@ -721,8 +722,8 @@ public boolean isClientEntityIdUsed() * * *- * Note that {@code clientIdAliasUsed} and {@code clientEntityIdUsed} are - * mutually exclusive. + * Note that {@code clientIdAliasUsed}, {@code clientEntityIdUsed}, and + * {@code metadataDocumentUsed} are mutually exclusive. *
* * @param used @@ -743,6 +744,77 @@ public TokenCreateRequest setClientEntityIdUsed(boolean used) } + /** + * Get the flag which indicates whether to emulate that the location of + * the client's metadata document is used as a client ID. + * + *+ * If the service supports OAuth Client ID Metadata Document + * (cf. {@link Service#isClientIdMetadataDocumentSupported()}), the + * location of the client's metadata document can be used as a client ID. + *
+ * + *+ * Note that {@code clientIdAliasUsed}, {@code clientEntityIdUsed}, and + * {@code metadataDocumentUsed} are mutually exclusive. + *
+ * + * @return + * {@code true} to emulate that the location of the client's + * metadata document is used as a client ID. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public boolean isMetadataDocumentUsed() + { + return metadataDocumentUsed; + } + + + /** + * Set the flag which indicates whether to emulate that the location of + * the client's metadata document is used as a client ID. + * + *+ * If the service supports OAuth Client ID Metadata Document + * (cf. {@link Service#isClientIdMetadataDocumentSupported()}), the + * location of the client's metadata document can be used as a client ID. + *
+ * + *+ * Note that {@code clientIdAliasUsed}, {@code clientEntityIdUsed}, and + * {@code metadataDocumentUsed} are mutually exclusive. + *
+ * + * @param used + * {@code true} to emulate that the location of the client's + * metadata document is used as a client ID. + * + * @return + * {@code this} object. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public TokenCreateRequest setMetadataDocumentUsed(boolean used) + { + this.metadataDocumentUsed = used; + + return this; + } + + /** * Get the access token. * diff --git a/src/main/java/com/authlete/common/dto/TokenFailRequest.java b/src/main/java/com/authlete/common/dto/TokenFailRequest.java index a834f51987..80ffb34a3d 100644 --- a/src/main/java/com/authlete/common/dto/TokenFailRequest.java +++ b/src/main/java/com/authlete/common/dto/TokenFailRequest.java @@ -63,37 +63,68 @@ public enum Reason * Unknown reason. * *- * Using this reason will result in - * {@code "error":"server_error"}. + * Using this reason will result in {@code "error":"server_error"}. *
*/ UNKNOWN, /** - * The resource owner's credentials ({@code username} and - * {@code password}) contained in the token request whose - * flow is - * "Resource Owner Password Credentials") are invalid. + * The resource owner's credentials ({@code username} and {@code password}) contained in the token request whose flow is "Resource + * Owner Password Credentials") are invalid. * *- * Using this reason will result in - * {@code "error":"invalid_request"}. + * Using this reason will result in {@code "error":"invalid_request"}. *
*/ INVALID_RESOURCE_OWNER_CREDENTIALS, /** - * The requested resource is invalid, missing, unknown, or malformed. - * See "Resource Indicators for OAuth 2.0" for details. + * The requested resource is invalid, missing, unknown, or malformed. See "Resource Indicators for OAuth 2.0" for details. * *- * Using this reason will result in - * {@code "error":"invalid_target"}. + * Using this reason will result in {@code "error":"invalid_target"}. *
* * @since 2.62 */ INVALID_TARGET, + + /** + * Client authentication failed (e.g., unknown client, no client authentication included, or unsupported + * authentication method). The authorization server MAY return an HTTP 401 (Unauthorized) status code to indicate + * which HTTP authentication schemes are supported. If the client attempted to authenticate via the "Authorization" + * request header field, the authorization server MUST respond with an HTTP 401 (Unauthorized) status code and + * include the "WWW-Authenticate" response header field matching the authentication scheme used by the client. + * + *+ * Using this reason will result in {@code "error":"invalid_client"}. + *
+ * + * @since 4.46 + */ + INVALID_CLIENT, + + /** + * The authenticated client is not authorized to use this authorization grant type. + * + *+ * Using this reason will result in {@code "error":"unauthorized_client"}. + *
+ * + * @since 4.46 + */ + UNAUTHORIZED_CLIENT, + + /** + * The requested scope is invalid, unknown, malformed, or exceeds the scope granted by the resource owner. + * + *+ * Using this reason will result in {@code "error":"invalid_scope"}. + *
+ * + * @since 4.46 + */ + INVALID_SCOPE, } diff --git a/src/main/java/com/authlete/common/dto/TokenInfo.java b/src/main/java/com/authlete/common/dto/TokenInfo.java index 20d0621f42..c087f8034a 100644 --- a/src/main/java/com/authlete/common/dto/TokenInfo.java +++ b/src/main/java/com/authlete/common/dto/TokenInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022-2023 Authlete, Inc. + * Copyright (C) 2022-2025 Authlete, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,7 +42,7 @@ */ public class TokenInfo implements Serializable { - private static final long serialVersionUID = 2L; + private static final long serialVersionUID = 3L; /** @@ -106,6 +106,25 @@ public class TokenInfo implements Serializable private boolean clientEntityIdUsed; + /** + * The location of the client's metadata document. + * + * @since 4.29 + * @since Authlete 3.0.22 + */ + private URI metadataDocumentLocation; + + + /** + * Flag which indicates whether the location of the client's metadata + * document was used when the request for the token was made. + * + * @since 4.29 + * @since Authlete 3.0.22 + */ + private boolean metadataDocumentUsed; + + /** * The target resources of the token. */ @@ -459,6 +478,110 @@ public TokenInfo setClientEntityIdUsed(boolean used) } + /** + * Get the location of the client's metadata document. This property + * holds a non-null value only when {@link #isMetadataDocumentUsed()} + * returns {@code true}. + * + * @return + * The location of the client's metadata document. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public URI getMetadataDocumentLocation() + { + return metadataDocumentLocation; + } + + + /** + * Set the location of the client's metadata document. This property + * should hold a non-null value only when {@link #isMetadataDocumentUsed()} + * returns {@code true}. + * + * @param location + * The location of the client's metadata document. + * + * @return + * {@code this} object. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public TokenInfo setMetadataDocumentLocation(URI location) + { + this.metadataDocumentLocation = location; + + return this; + } + + + /** + * Get the flag which indicates whether the location of the client's + * metadata document was used as a client ID. + * + *+ * This can happen when the service supports OAuth Client ID Metadata Document. + * (cf. {@link Service#isClientIdMetadataDocumentSupported()}) + *
+ * + * @return + * {@code true} if the location of client's metadata document + * was used as a client ID. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public boolean isMetadataDocumentUsed() + { + return metadataDocumentUsed; + } + + + /** + * Set the flag which indicates whether the location of the client's + * metadata document was used as a client ID. + * + *+ * This can happen when the service supports OAuth Client ID Metadata Document. + * (cf. {@link Service#isClientIdMetadataDocumentSupported()}) + *
+ * + * @param used + * {@code true} to indicate that the location of the client's + * metadata document was used as a client ID. + * + * @return + * {@code this} object. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public TokenInfo setMetadataDocumentUsed(boolean used) + { + this.metadataDocumentUsed = used; + + return this; + } + + /** * Get the resources associated with the token. * diff --git a/src/main/java/com/authlete/common/dto/TokenIssueResponse.java b/src/main/java/com/authlete/common/dto/TokenIssueResponse.java index 823f6376ff..8a7423548e 100644 --- a/src/main/java/com/authlete/common/dto/TokenIssueResponse.java +++ b/src/main/java/com/authlete/common/dto/TokenIssueResponse.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2023 Authlete, Inc. + * Copyright (C) 2014-2025 Authlete, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -103,7 +103,7 @@ */ public class TokenIssueResponse extends ApiResponse { - private static final long serialVersionUID = 10L; + private static final long serialVersionUID = 11L; /** @@ -202,6 +202,22 @@ public enum Action */ private boolean clientEntityIdUsed; + /** + * The location of the client's metadata document. + * + * @since 4.29 + * @since Authlete 3.0.22 + */ + private URI metadataDocumentLocation; + + /** + * Whether the client's metadata document was used. + * + * @since 4.29 + * @since Authlete 3.0.22 + */ + private boolean metadataDocumentUsed; + /** * @since Authlete 1.1 */ @@ -685,6 +701,100 @@ public void setClientEntityIdUsed(boolean used) } + /** + * Get the location of the client's metadata document. This property + * holds a non-null value only when {@link #isMetadataDocumentUsed()} + * returns {@code true}. + * + * @return + * The location of the client's metadata document. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public URI getMetadataDocumentLocation() + { + return metadataDocumentLocation; + } + + + /** + * Set the location of the client's metadata document. This property + * should hold a non-null value only when {@link #isMetadataDocumentUsed()} + * returns {@code true}. + * + * @param location + * The location of the client's metadata document. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public void setMetadataDocumentLocation(URI location) + { + this.metadataDocumentLocation = location; + } + + + /** + * Get the flag which indicates whether the location of the client's + * metadata document was used as a client ID. + * + *+ * This can happen when the service supports OAuth Client ID Metadata Document. + * (cf. {@link Service#isClientIdMetadataDocumentSupported()}) + *
+ * + * @return + * {@code true} if the location of client's metadata document + * was used as a client ID. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public boolean isMetadataDocumentUsed() + { + return metadataDocumentUsed; + } + + + /** + * Set the flag which indicates whether the location of the client's + * metadata document was used as a client ID. + * + *+ * This can happen when the service supports OAuth Client ID Metadata Document. + * (cf. {@link Service#isClientIdMetadataDocumentSupported()}) + *
+ * + * @param used + * {@code true} to indicate that the location of the client's + * metadata document was used as a client ID. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public void setMetadataDocumentUsed(boolean used) + { + this.metadataDocumentUsed = used; + } + + /** * Get the subject (= resource owner's ID) of the access token. * diff --git a/src/main/java/com/authlete/common/dto/TokenRequest.java b/src/main/java/com/authlete/common/dto/TokenRequest.java index f0be40df27..46a68f24ca 100644 --- a/src/main/java/com/authlete/common/dto/TokenRequest.java +++ b/src/main/java/com/authlete/common/dto/TokenRequest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2024 Authlete, Inc. + * Copyright (C) 2014-2025 Authlete, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -212,7 +212,7 @@ */ public class TokenRequest implements Serializable { - private static final long serialVersionUID = 12L; + private static final long serialVersionUID = 13L; /** @@ -346,6 +346,15 @@ public class TokenRequest implements Serializable private String oauthClientAttestationPop; + /** + * Options for CIMD processing. + * + * @since 4.30 + * @since Authlete 3.0.22 + */ + private CimdOptions cimdOptions; + + /** * Get the value of {@code parameters} which are the request * parameters that the OAuth 2.0 token endpoint of the service @@ -1247,4 +1256,49 @@ public TokenRequest setOauthClientAttestationPop(String jwt) return this; } + + + /** + * Get options for CIMD processing. + * + * @return + * Options for CIMD processing. + * + * @since 4.30 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public CimdOptions getCimdOptions() + { + return cimdOptions; + } + + + /** + * Set options for CIMD processing. + * + * @param options + * Options for CIMD processing. + * + * @return + * {@code this} object. + * + * @since 4.30 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public TokenRequest setCimdOptions(CimdOptions options) + { + this.cimdOptions = options; + + return this; + } } diff --git a/src/main/java/com/authlete/common/dto/TokenResponse.java b/src/main/java/com/authlete/common/dto/TokenResponse.java index 7a15841744..16fc38d69b 100644 --- a/src/main/java/com/authlete/common/dto/TokenResponse.java +++ b/src/main/java/com/authlete/common/dto/TokenResponse.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2025 Authlete, Inc. + * Copyright (C) 2014-2026 Authlete, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -1165,6 +1165,33 @@ * 2.0 Demonstrating Proof of Possession (DPoP) for details. * * + *+ * Since version 3.0.28, Authlete recognizes the {@code challenge} claim in + * Client Attestation PoP JWTs. If the {@code challenge} claim is required + * (= if the service's challenge endpoint is configured), the Authlete API + * checks whether the {@code challenge} claim in the presented Client + * Attestation PoP JWT is identical to the expected value. + *
+ * + *+ * If the {@code attestationChallenge} response parameter from the API is not + * null, its value is the expected attestation challenge value for Client + * Attestation PoP JWT. The expected value needs to be conveyed to the client + * application as the value of the {@code OAuth-Client-Attestation-Challenge} + * HTTP header. + *
+ * + *OAuth-Client-Attestation-Challenge: (The value returned from {@link #getAttestationChallenge()})
+ *
+ * + * See OAuth 2.0 Attestation-Based Client Authentication for details. + *
+ * * @see RFC 6749 The OAuth 2.0 Authorization Framework * @@ -1184,10 +1211,13 @@ * * @see OpenID Connect Native SSO for Mobile Apps 1.0 + * + * @see + * OAuth 2.0 Attestation-Based Client Authentication */ public class TokenResponse extends ApiResponse { - private static final long serialVersionUID = 21L; + private static final long serialVersionUID = 23L; /** @@ -1414,6 +1444,22 @@ public enum Action */ private boolean clientEntityIdUsed; + /** + * The location of the client's metadata document. + * + * @since 4.29 + * @since Authlete 3.0.22 + */ + private URI metadataDocumentLocation; + + /** + * Whether the client's metadata document was used. + * + * @since 4.29 + * @since Authlete 3.0.22 + */ + private boolean metadataDocumentUsed; + /** * @since Authlete 1.1.9 */ @@ -1598,6 +1644,12 @@ public enum Action */ private String sessionId; + /** + * Additional claims that need to be added to the ID token. + * This field contains a JSON string with claims like nonce and s_hash. + */ + private String additionalClaims; + /** * The device secret. * @@ -1614,6 +1666,16 @@ public enum Action */ private String deviceSecretHash; + /** + * The expected attestation challenge value for Client Attestation PoP JWT, + * which should be used as the value of the + * {@code OAuth-Client-Attestation-Challenge} HTTP header. + * + * @since 4.39 + * @since Authlete 3.0.28 + */ + private String attestationChallenge; + /** * Get the next action that the service implementation should take. @@ -2206,6 +2268,100 @@ public void setClientEntityIdUsed(boolean used) } + /** + * Get the location of the client's metadata document. This property + * holds a non-null value only when {@link #isMetadataDocumentUsed()} + * returns {@code true}. + * + * @return + * The location of the client's metadata document. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public URI getMetadataDocumentLocation() + { + return metadataDocumentLocation; + } + + + /** + * Set the location of the client's metadata document. This property + * should hold a non-null value only when {@link #isMetadataDocumentUsed()} + * returns {@code true}. + * + * @param location + * The location of the client's metadata document. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public void setMetadataDocumentLocation(URI location) + { + this.metadataDocumentLocation = location; + } + + + /** + * Get the flag which indicates whether the location of the client's + * metadata document was used as a client ID. + * + *+ * This can happen when the service supports OAuth Client ID Metadata Document. + * (cf. {@link Service#isClientIdMetadataDocumentSupported()}) + *
+ * + * @return + * {@code true} if the location of client's metadata document + * was used as a client ID. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public boolean isMetadataDocumentUsed() + { + return metadataDocumentUsed; + } + + + /** + * Set the flag which indicates whether the location of the client's + * metadata document was used as a client ID. + * + *+ * This can happen when the service supports OAuth Client ID Metadata Document. + * (cf. {@link Service#isClientIdMetadataDocumentSupported()}) + *
+ * + * @param used + * {@code true} to indicate that the location of the client's + * metadata document was used as a client ID. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public void setMetadataDocumentUsed(boolean used) + { + this.metadataDocumentUsed = used; + } + + /** * Get the subject (= resource owner's ID) of the access token. * @@ -3556,6 +3712,44 @@ public void setSessionId(String sessionId) } + /** + * Get the additional claims that need to be added to the ID token. + * + *+ * This field contains a JSON string with claims like nonce and s_hash + * that need to be included in the ID token when the action is NATIVE_SSO. + *
+ * + * @return + * The additional claims as a JSON string. + */ + public String getAdditionalClaims() + { + return additionalClaims; + } + + + /** + * Set the additional claims that need to be added to the ID token. + * + *+ * This field should contain a JSON string with claims like nonce and s_hash + * that need to be included in the ID token when the action is NATIVE_SSO. + *
+ * + * @param additionalClaims + * The additional claims as a JSON string. + * + * @return + * {@code this} object. + */ + public TokenResponse setAdditionalClaims(String additionalClaims) + { + this.additionalClaims = additionalClaims; + return this; + } + + /** * Get the device secret presented in the token request. This is available * only when the authorization server supports the + * When this response parameter is not null, the implementation of the + * token endpoint should add the {@code OAuth-Client-Attestation-Challenge} + * HTTP header in the response from the endpoint to the client application, + * using the value of this response parameter as the value of the HTTP header. + * + * + *
+ * OAuth-Client-Attestation-Challenge: (The value of this {@code attestationChallenge} response parameter)
+ *
+ *
+ * @return
+ * The expected attestation challenge value for Client Attestation
+ * PoP JWT.
+ *
+ * @since 4.39
+ * @since Authlete 3.0.28
+ *
+ * @see
+ * OAuth 2.0 Attestation-Based Client Authentication
+ */
+ public String getAttestationChallenge()
+ {
+ return attestationChallenge;
+ }
+
+
+ /**
+ * Set the expected attestation challenge value for Client Attestation PoP
+ * JWT, which should be used as the value of the
+ * {@code OAuth-Client-Attestation-Challenge} HTTP header.
+ *
+ * + * When this response parameter is not null, the implementation of the + * token endpoint should add the {@code OAuth-Client-Attestation-Challenge} + * HTTP header in the response from the endpoint to the client application, + * using the value of this response parameter as the value of the HTTP header. + *
+ * + *
+ * OAuth-Client-Attestation-Challenge: (The value of this {@code attestationChallenge} response parameter)
+ *
+ *
+ * @param challenge
+ * The expected attestation challenge value for Client Attestation
+ * PoP JWT.
+ *
+ * @since 4.39
+ * @since Authlete 3.0.28
+ *
+ * @see
+ * OAuth 2.0 Attestation-Based Client Authentication
+ */
+ public void setAttestationChallenge(String challenge)
+ {
+ this.attestationChallenge = challenge;
+ }
}
diff --git a/src/main/java/com/authlete/common/dto/TokenRevokeRequest.java b/src/main/java/com/authlete/common/dto/TokenRevokeRequest.java
index a5ed98fdea..90303484bc 100644
--- a/src/main/java/com/authlete/common/dto/TokenRevokeRequest.java
+++ b/src/main/java/com/authlete/common/dto/TokenRevokeRequest.java
@@ -79,6 +79,14 @@
* the API returns {@code 400 Bad Request}.
*
*
+ *
+ * + * Bulk revocation with {@code clientIdentifier} only, {@code clientIdentifier} + {@code subject}, + * or {@code subject} only deletes at most 20 tokens per request + * (the default of {@code token.revoke.count.max} in {@code ServerConfiguration.java}). If the + * target has more than 20 tokens, the response {@code count} will be 20 and the remainder + * is left untouched. To fully wipe them, call the endpoint repeatedly until {@code count} returns 0. + *
* * @since 3.26 * @since Authlete 2.2.29 diff --git a/src/main/java/com/authlete/common/dto/TokenRevokeResponse.java b/src/main/java/com/authlete/common/dto/TokenRevokeResponse.java index 3286520b27..2b16591fba 100644 --- a/src/main/java/com/authlete/common/dto/TokenRevokeResponse.java +++ b/src/main/java/com/authlete/common/dto/TokenRevokeResponse.java @@ -26,12 +26,19 @@ public class TokenRevokeResponse extends ApiResponse { private static final long serialVersionUID = 1L; - + /** + * The maximum amount of tokens that can be revoked per request is 20. + * If 20 is returned then additional token revocation requests should be made until this value + * is 0 to ensure that all tokens are revoked. + */ private int count; /** * Get the number of revoked tokens. + * The maximum amount of tokens that can be revoked per request is 20. + * If 20 is returned then additional token revocation requests should be made until this value + * is 0 to ensure that all tokens are revoked. * * @return * The number of revoked tokens. @@ -44,6 +51,9 @@ public int getCount() /** * Set the number of revoked tokens. + * The maximum amount of tokens that can be revoked per request is 20. + * If 20 is returned then additional token revocation requests should be made until this value + * is 0 to ensure that all tokens are revoked. * * @param count * The number of revoked tokens. diff --git a/src/main/java/com/authlete/common/dto/TokenUpdateRequest.java b/src/main/java/com/authlete/common/dto/TokenUpdateRequest.java index 1435639af2..a9afe91398 100644 --- a/src/main/java/com/authlete/common/dto/TokenUpdateRequest.java +++ b/src/main/java/com/authlete/common/dto/TokenUpdateRequest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016-2023 Authlete, Inc. + * Copyright (C) 2016-2026 Authlete, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -90,6 +90,28 @@ * * * + *refreshToken+ * The refresh token value associated with the access token to update. + *
+ *+ * The value of the {@code accessToken} parameter and the {@code accessTokenHash} + * parameter take precedence. + *
+ *refreshTokenHash+ * The hash of the refresh token value associated with the access token to update. + *
+ *+ * The value of the {@code accessToken} parameter, the {@code accessTokenHash} parameter + * and the {@code refreshToken} parameter take precedence. + *
+ *accessTokenValueUpdated@@ -152,7 +174,7 @@ */ public class TokenUpdateRequest implements Serializable { - private static final long serialVersionUID = 8L; + private static final long serialVersionUID = 9L; private String accessToken; @@ -170,6 +192,8 @@ public class TokenUpdateRequest implements Serializable private AuthzDetails authorizationDetails; private boolean forExternalAttachment; private String tokenId; + private String refreshToken; + private String refreshTokenHash; /** @@ -928,4 +952,93 @@ public TokenUpdateRequest setTokenId(String tokenId) return this; } + + + /** + * Get the refresh token associated with the access token to update. + * + *
+ * The value of the {@code accessToken} parameter and the {@code accessTokenHash} + * parameter take precedence. + *
+ * + * @return + * The refresh token associated with the access token to update. + * + * @since 4.38 + */ + public String getRefreshToken() + { + return refreshToken; + } + + + /** + * Set the refresh token associated with the access token to update. + * + *+ * The value of the {@code accessToken} parameter and the {@code accessTokenHash} + * parameter take precedence. + *
+ * + * @param refreshToken + * The refresh token associated with the access token to update. + * + * @return + * {@code this} object. + * + * @since 4.38 + */ + public TokenUpdateRequest setRefreshToken(String refreshToken) + { + this.refreshToken = refreshToken; + + return this; + } + + + /** + * Get the hash of the refresh token value associated with the access token to update. + * + *+ * The value of the {@code accessToken} parameter, the {@code accessTokenHash} + * parameter and the {@code refreshToken} parameter take precedence. + *
+ * + * @return + * The hash of the refresh token value associated with the access token to + * update. + * + * @since 4.38 + */ + public String getRefreshTokenHash() + { + return refreshTokenHash; + } + + + /** + * Set the hash of the refresh token value associated with the access + * token to update. + * + *+ * The value of the {@code accessToken} parameter, the {@code accessTokenHash} + * parameter and the {@code refreshToken} parameter take precedence. + *
+ * + * @param refreshTokenHash + * The hash of the refresh token value associated with the + * access token to update. + * + * @return + * {@code this} object. + * + * @since 4.38 + */ + public TokenUpdateRequest setRefreshTokenHash(String refreshTokenHash) + { + this.refreshTokenHash = refreshTokenHash; + + return this; + } } diff --git a/src/main/java/com/authlete/common/dto/UserInfoResponse.java b/src/main/java/com/authlete/common/dto/UserInfoResponse.java index dc15d2efb9..6820f70e16 100644 --- a/src/main/java/com/authlete/common/dto/UserInfoResponse.java +++ b/src/main/java/com/authlete/common/dto/UserInfoResponse.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2023 Authlete, Inc. + * Copyright (C) 2015-2025 Authlete, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -247,7 +247,7 @@ */ public class UserInfoResponse extends ApiResponse { - private static final long serialVersionUID = 7L; + private static final long serialVersionUID = 8L; /** @@ -392,6 +392,25 @@ public enum Action private boolean clientEntityIdUsed; + /** + * The location of the client's metadata document. + * + * @since 4.29 + * @since Authlete 3.0.22 + */ + private URI metadataDocumentLocation; + + + /** + * Flag which indicates whether the location of the client's metadata + * document was used when the request for the access token was made. + * + * @since 4.29 + * @since Authlete 3.0.22 + */ + private boolean metadataDocumentUsed; + + /** * "userinfo" in "claims" of an authorization request. * @since Authlete 2.2.1 @@ -801,6 +820,100 @@ public void setClientEntityIdUsed(boolean used) } + /** + * Get the location of the client's metadata document. This property + * holds a non-null value only when {@link #isMetadataDocumentUsed()} + * returns {@code true}. + * + * @return + * The location of the client's metadata document. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public URI getMetadataDocumentLocation() + { + return metadataDocumentLocation; + } + + + /** + * Set the location of the client's metadata document. This property + * should hold a non-null value only when {@link #isMetadataDocumentUsed()} + * returns {@code true}. + * + * @param location + * The location of the client's metadata document. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public void setMetadataDocumentLocation(URI location) + { + this.metadataDocumentLocation = location; + } + + + /** + * Get the flag which indicates whether the location of the client's + * metadata document was used as a client ID. + * + *+ * This can happen when the service supports OAuth Client ID Metadata Document. + * (cf. {@link Service#isClientIdMetadataDocumentSupported()}) + *
+ * + * @return + * {@code true} if the location of client's metadata document + * was used as a client ID. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public boolean isMetadataDocumentUsed() + { + return metadataDocumentUsed; + } + + + /** + * Set the flag which indicates whether the location of the client's + * metadata document was used as a client ID. + * + *+ * This can happen when the service supports OAuth Client ID Metadata Document. + * (cf. {@link Service#isClientIdMetadataDocumentSupported()}) + *
+ * + * @param used + * {@code true} to indicate that the location of the client's + * metadata document was used as a client ID. + * + * @since 4.29 + * @since Authlete 3.0.22 + * + * @see + * OAuth Client ID Metadata Document + */ + public void setMetadataDocumentUsed(boolean used) + { + this.metadataDocumentUsed = used; + } + + /** * Get the value of the {@code "userinfo"} property in the {@code "claims"} * request parameter or in the {@code "claims"} property in an authorization diff --git a/src/main/java/com/authlete/common/package-info.java b/src/main/java/com/authlete/common/package-info.java index b50466ba76..ca6e225b94 100644 --- a/src/main/java/com/authlete/common/package-info.java +++ b/src/main/java/com/authlete/common/package-info.java @@ -17,11 +17,11 @@ * <dependency> * <groupId>com.authlete</groupId> * <artifactId>authlete-java-common</artifactId> - * <version>4.21</version> + * <version>4.23</version> * </dependency> * - * @version 4.21 + * @version 4.23 */ package com.authlete.common; diff --git a/src/main/java/com/authlete/common/types/ClientAssertionType.java b/src/main/java/com/authlete/common/types/ClientAssertionType.java index ce3f877cac..450777d9b5 100644 --- a/src/main/java/com/authlete/common/types/ClientAssertionType.java +++ b/src/main/java/com/authlete/common/types/ClientAssertionType.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2024 Authlete, Inc. + * Copyright (C) 2023-2026 Authlete, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -64,6 +64,22 @@ public enum ClientAssertionType * @deprecated */ JWT_CLIENT_ATTESTATION((short)2, "urn:ietf:params:oauth:client-assertion-type:jwt-client-attestation"), + + + /** + * {@code "urn:ietf:params:oauth:client-assertion-type:jwt-spiffe"}. + * + *+ * The client assertion type used by the OAuth SPIFFE Client Authentication. + *
+ * + * @see + * OAuth SPIFFE Client Authentication + * + * @since 4.37 + * @since Authlete 3.0.28 + */ + JWT_SPIFFE((short)3, "urn:ietf:params:oauth:client-assertion-type:jwt-spiffe"), ; diff --git a/src/main/java/com/authlete/common/types/ClientAuthMethod.java b/src/main/java/com/authlete/common/types/ClientAuthMethod.java index 56645e282c..7a192dc9ef 100644 --- a/src/main/java/com/authlete/common/types/ClientAuthMethod.java +++ b/src/main/java/com/authlete/common/types/ClientAuthMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2024 Authlete, Inc. + * Copyright (C) 2014-2026 Authlete, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -157,6 +157,22 @@ public enum ClientAuthMethod * >OAuth 2.0 Attestation-Based Client Authentication */ ATTEST_JWT_CLIENT_AUTH((short)7, "attest_jwt_client_auth", 0x2), + + + /** + * {@code "spiffe_jwt"} (8). + * + *+ * OAuth SPIFFE Client Authentication using JWT-SVID. + *
+ * + * @since 4.36 + * @since Authlete 3.0.28 + * + * @see + * OAuth SPIFFE Client Authentication + */ + SPIFFE_JWT((short)8, "spiffe_jwt", 0x2), ; diff --git a/src/main/java/com/authlete/common/types/ClientSource.java b/src/main/java/com/authlete/common/types/ClientSource.java new file mode 100644 index 0000000000..0db40b51a0 --- /dev/null +++ b/src/main/java/com/authlete/common/types/ClientSource.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2025 Authlete, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.authlete.common.types; + + +/** + * The source of a client. + * + *+ *+ * + * @since 4.29 + * @since Authlete 3.0.22 + */ +public enum ClientSource +{ + /** + * Static registration. + */ + STATIC_REGISTRATION, + + + /** + * Dynamic registration defined in + * RFC 7591: OAuth + * 2.0 Dynamic Client Registration Protocol or + * OpenID Connect Dynamic Client Registration 1.0. + * + * @see + * RFC 7591: OAuth 2.0 Dynamic Client Registration Protocol + * + * @see + * OpenID Connect Dynamic Client Registration 1.0 + */ + DYNAMIC_REGISTRATION, + + + /** + * Automatic registration defined in + * OpenID Federation 1.0. + * + * @see + * OpenID Federation 1.0 + */ + AUTOMATIC_REGISTRATION, + + + /** + * Explicit registration defined in + * OpenID Federation 1.0. + * + * @see + * OpenID Federation 1.0 + */ + EXPLICIT_REGISTRATION, + + + /** + * Discovery using + * OAuth Client ID Metadata Document. + * + * @see + * OAuth Client ID Metadata Document + */ + METADATA_DOCUMENT, + ; +} diff --git a/src/main/java/com/authlete/common/types/ErrorCode.java b/src/main/java/com/authlete/common/types/ErrorCode.java index 0cb8f13450..76cc10bca8 100644 --- a/src/main/java/com/authlete/common/types/ErrorCode.java +++ b/src/main/java/com/authlete/common/types/ErrorCode.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2023 Authlete, Inc. + * Copyright (C) 2014-2026 Authlete, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,7 +44,11 @@ * @see RFC 7591, 3.2.2. Client Registration Error Response * - * @author Takahiko Kawasaki + * @see OpenID for Verifiable Credential Issuance 1.0, 8.3.1.2. Credential Request Errors + * + * @see OpenID for Verifiable Credential Issuance 1.0, 11.3. Notification Error Response */ public enum ErrorCode { @@ -620,5 +624,124 @@ public enum ErrorCode * @since Authlete 3.0 */ use_dpop_nonce, + + + /** + * Requested Credential Configuration is unknown. + * + * @see + * OpenID for Verifiable Credential Issuance 1.0, 8.3.1.2. Credential Request Errors + * + * @since 4.28 + * @since Authlete 3.0.22 + */ + unknown_credential_configuration, + + + /** + * Requested Credential identifier is unknown. + * + * @see + * OpenID for Verifiable Credential Issuance 1.0, 8.3.1.2. Credential Request Errors + * + * @since 4.28 + * @since Authlete 3.0.22 + */ + unknown_credential_identifier, + + + /** + * The {@code proofs} parameter in the Credential Request uses an invalid + * nonce: at least one of the key proofs contains an invalid {@code c_nonce} + * value. The wallet should retrieve a new {@code c_nonce value} (refer to + * Section 7). + * + * @see + * OpenID for Verifiable Credential Issuance 1.0, 8.3.1.2. Credential Request Errors + * + * @since 4.28 + * @since Authlete 3.0.22 + */ + invalid_nonce, + + + /** + * The Credential Request has not been accepted by the Credential Issuer. + * The Wallet SHOULD treat this error as unrecoverable, meaning if + * received from a Credential Issuer the Credential cannot be issued. + * + * @see + * OpenID for Verifiable Credential Issuance 1.0, 8.3.1.2. Credential Request Errors + * + * @since 4.28 + * @since Authlete 3.0.22 + */ + credential_request_denied, + + + /** + * The {@code notification_id} in the Notification Request was invalid. + * + * @see + * OpenID for Verifiable Credential Issuance 1.0, 11.3. Notification Error Response + * + * @since 4.28 + * @since Authlete 3.0.22 + */ + invalid_notification_id, + + + /** + * The Notification Request is missing a required parameter, includes an + * unsupported parameter or parameter value, repeats the same parameter, + * or is otherwise malformed. + * + * @see + * OpenID for Verifiable Credential Issuance 1.0, 11.3. Notification Error Response + * + * @since 4.28 + * @since Authlete 3.0.22 + */ + invalid_notification_request, + + + /** + * The Client Attestation PoP JWT is not using an expected server-provided + * challenge. + * + * @see + * OAuth 2.0 Attestation-Based Client Authentication + * + * @since 4.39 + * @since Authlete 3.0.28 + */ + use_attestation_challenge, + + + /** + * The Client Attestation JWT is deemed to be not fresh enough to be + * accepted by the server. + * + * @see + * OAuth 2.0 Attestation-Based Client Authentication + * + * @since 4.39 + * @since Authlete 3.0.28 + */ + use_fresh_attestation, + + + /** + * The attestation or its proof of possession could not be successfully + * verified. + * + * @see + * OAuth 2.0 Attestation-Based Client Authentication + * + * @since 4.39 + * @since Authlete 3.0.28 + */ + invalid_client_attestation, ; } diff --git a/src/main/java/com/authlete/common/types/JWEZip.java b/src/main/java/com/authlete/common/types/JWEZip.java new file mode 100644 index 0000000000..512e75407f --- /dev/null +++ b/src/main/java/com/authlete/common/types/JWEZip.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2025 Authlete, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.authlete.common.types; + + +import java.util.EnumSet; + + +/** + * {@code "zip"} (Compression Algorithm) Header Parameter Values for JWE. + * + * @see + * RFC 7516: JSON Web Encryption (JWE), + * Section 4.1.3. "zip" (Compression Algorithm) Header Parameter + * + * @see + * RFC 7518: JSON Web Algorithms (JWA), + * Section 7.3. JSON Web Encryption Compression Algorithms Registry + * + * @see + * IANA: JSON Object Signing and Encryption (JOSE), + * JSON Web Encryption Compression Algorithms + * + * @since 4.26 + */ +public enum JWEZip +{ + /** + * {@code "DEF"} (1); DEFLATE. + */ + DEF((short)1, "DEF"), + ; + + + private static final JWEZip[] sValues = values(); + private static final Helper sHelper = new Helper(sValues); + private final short mValue; + private final String mString; + + + private JWEZip(short value, String string) + { + mValue = value; + mString = string; + } + + + /** + * Get the name of this algorithm. + * + * One of the values listed in the table in + * JSON Web Encryption Compression Algorithms. + * + * @return + * The name of this algorithm. + */ + public String getName() + { + return mString; + } + + + /** + * Get the integer representation of this enum instance. + */ + public short getValue() + { + return mValue; + } + + + @Override + public String toString() + { + return mString; + } + + + /** + * Find an instance of this enum by a value. + * + * @param value + * The integer representation of the instance to find. + * + * @return + * An instance of this enum, or {@code null} if not found. + */ + public static JWEZip getByValue(short value) + { + if (value < 1 || sValues.length < value) + { + // Not found. + return null; + } + + return sValues[value - 1]; + } + + + /** + * Convert {@code String} to {@code JWEZip}. + * + * @param alg + * Algorithm name. For example, {@code "DEF"}. + * + * @return + * {@code JWEZip} instance, or {@code null}. + */ + public static JWEZip parse(String alg) + { + if (alg == null) + { + return null; + } + + for (JWEZip entry : sValues) + { + if (entry.mString.equals(alg)) + { + // Found. + return entry; + } + } + + // Not found. + return null; + } + + + public static int toBits(EnumSet+ *
+ *+ * + *enum value + *description + *+ * + *{@link #STATIC_REGISTRATION} + *Static registration. + *+ * + *{@link #DYNAMIC_REGISTRATION} + *+ * Dynamic registration defined in + * RFC 7591: + * OAuth 2.0 Dynamic Client Registration Protocol or + * + * OpenID Connect Dynamic Client Registration 1.0. + * + *+ * + *{@link #AUTOMATIC_REGISTRATION} + *+ * Automatic registration defined in + * OpenID + * Federation 1.0. + * + *+ * + *{@link #EXPLICIT_REGISTRATION} + *+ * Explicit registration defined in + * OpenID + * Federation 1.0. + * + *+ * + *{@link #METADATA_DOCUMENT} + *+ * Discovery using + * + * OAuth Client ID Metadata Document. + * + *
+ * {@code update} methods are provided for all the primitive types + * and {@code String}, and their array types. In addition, + * {@link #updateJson(String)} has been available since the version + * 1.2 which updates the digest with the content of the given JSON. + * Note that {@link #update(String)} and {@link #updateJson(String)} + * are different. + *
+ * + *
+ * getInstanceXXX methods (where XXX
+ * is a pre-defined algorithm name with hyphens removed) such as
+ * {@link #getInstanceSHA1()} are provided. They won't throw
+ * {@code NoSuchAlgorithmException}.
+ *
+ * + * // Compute SHA-1 of "Hello, world.". + * // 'digest' will have "2ae01472317d1935a84797ec1983ae243fc6aa28". + * String digest = Digest.{@link #getInstanceSHA1()} + * .{@link #update(String) update}("Hello, world.") + * .{@link #digestAsString()}; + * + * // Compute SHA-1 of "Hello, world." and get the result as Base64. + * // 'digest' will have "KuAUcjF9GTWoR5fsGYOuJD/Gqig=". + * String digest = Digest.{@link #getInstanceSHA1()} + * .{@link #update(String) update}("Hello, world.") + * .{@link #digestAsString(BinaryEncoder) digestAsString}(new {@link + * org.apache.commons.codec.binary.Base64#Base64() Base64()}); + * + * // Compute SHA-1 of two JSONs. + * // 'result1' and 'result2' will have the same value. + * String json1 = "{ \"key1\":\"value1\", \"key2\":\"value2\" }"; + * String json2 = "{ \"key2\":\"value2\", \"key1\":\"value1\" }"; + * String result1 = Digest.{@link #getInstanceSHA1()}.{@link #updateJson(String) + * updateJson}(json1).{@link #digestAsString()}; + * String result2 = Digest.{@link #getInstanceSHA1()}.{@link #updateJson(String) + * updateJson}(json2).{@link #digestAsString()}; + *+ * + * @author Takahiko Kawasaki + * @since 4.23 + */ +public class Digest implements Cloneable +{ + /** + * Features to control behaviors. + * + */ + public static enum Feature + { + /** + * Ignore JSON key-value entries whose value is {@code null}. + * In other words, JSON key-value entries whose value is + * {@code null} are treated as if they did not exist. + * + *
+ * If this feature is enabled, two JSONs below generate + * the same digest value. + *
+ * + *
+ * { "key1":"value1", "key2":null }
+ * { "key1":"value1" }
+ *
+ *
+ * + * The default value is 'disabled'. + *
+ */ + IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_NULL, + + + /** + * Ignore JSON key-value entries whose value is {@code false}. + * In other words, JSON key-value entries whose value is + * {@code false} are treated as if they did not exist. + * + *+ * If this feature is enabled, two JSONs below generate + * the same digest value. + *
+ * + *
+ * { "key1":"value1", "key2":false }
+ * { "key1":"value1" }
+ *
+ *
+ * + * The default value is 'disabled'. + *
+ */ + IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_FALSE, + + + /** + * Ignore JSON key-value entries whose value is zero. + * In other words, JSON key-value entries whose value is + * zero are treated as if they did not exist. + * + *+ * If this feature is enabled, two JSONs below generate + * the same digest value. + *
+ * + *
+ * { "key1":"value1", "key2":0, "key3":0.0 }
+ * { "key1":"value1" }
+ *
+ *
+ * + * The default value is 'disabled'. + *
+ */ + IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_ZERO, + + + /** + * Ignore JSON key-value entries whose value is an empty string. + * In other words, JSON key-value entries whose value is an empty + * string are treated as if they did not exist. + * + *+ * If this feature is enabled, two JSONs below generate + * the same digest value. + *
+ * + *
+ * { "key1":"value1", "key2":"" }
+ * { "key1":"value1" }
+ *
+ *
+ * + * The default value is 'disabled'. + *
+ */ + IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_EMPTY_STRING, + + + /** + * Ignore JSON key-value entries whose value is an empty array. + * In other words, JSON key-value entries whose value is an empty + * array are treated as if they did not exist. + * + *+ * If this feature is enabled, two JSONs below generate + * the same digest value. + *
+ * + *
+ * { "key1":"value1", "key2":[] }
+ * { "key1":"value1" }
+ *
+ *
+ * + * The default value is 'disabled'. + *
+ */ + IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_EMPTY_ARRAY, + + + /** + * Ignore JSON key-value entries whose value is an empty object. + * In other words, JSON key-value entries whose value is an empty + * object are treated as if they did not exist. + * + *+ * If this feature is enabled, two JSONs below generate + * the same digest value. + *
+ * + *
+ * { "key1":"value1", "key2":{} }
+ * { "key1":"value1" }
+ *
+ *
+ * + * The default value is 'disabled'. + *
+ */ + IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_EMPTY_OBJECT, + + + /** + * Sort keys of JSON key-value entries before updating. + * + *+ * If this feature is enabled, two JSONs below generate + * the same digest value. + *
+ * + *
+ * { "key1":"value1", "key2":"value2" }
+ * { "key2":"value2", "key1":"value1" }
+ *
+ *
+ * + * The default value is 'enabled'. + *
+ */ + SORT_JSON_OBJECT_ENTRY_KEYS + } + + + /** + * Characters used to generate a hex string. + */ + private static final char[] mHexArray = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + + /** + * The wrapped messaged digest object. + */ + private MessageDigest mMessageDigest; + + + /** + * Features (configuration). + */ + private HashMap+ * This constructor is equivalent to {@link #Digest(MessageDigest) this}{@code + * (}{@link MessageDigest#getInstance(String) MessageDigest.getInstance}{@code + * (algorithm))}. + *
+ * + * @param algorithm + * Algorithm name such as "MD5" and "SHA-1". + * + * @throws NoSuchAlgorithmException + * No provider supports the specified algorithm. + */ + public Digest(String algorithm) throws NoSuchAlgorithmException + { + this(MessageDigest.getInstance(algorithm)); + } + + + /** + * Constructor with an algorithm name and a provider. + * + *+ * This constructor is equivalent to {@link #Digest(MessageDigest) this}{@code + * (}{@link MessageDigest#getInstance(String,String) MessageDigest.getInstance}{@code + * (algorithm, provider))}. + *
+ * + * @param algorithm + * Algorithm name such as "MD5" and "SHA-1". + * + * @param provider + * Provider name. + * + * @throws NoSuchAlgorithmException + * The provider does not support the specified algorithm. + * + * @throws NoSuchProviderException + * The specified provider is not registered in the security provider list. + */ + public Digest(String algorithm, String provider) throws NoSuchAlgorithmException, NoSuchProviderException + { + this(MessageDigest.getInstance(algorithm, provider)); + } + + + /** + * Constructor with an algorithm name and a provider. + * + *+ * This constructor is equivalent to {@link #Digest(MessageDigest) this}{@code + * (}{@link MessageDigest#getInstance(String,Provider) MessageDigest.getInstance}{@code + * (algorithm, provider))}. + *
+ * + * @param algorithm + * Algorithm name such as "MD5" and "SHA-1". + * + * @param provider + * Provider. + * + * @throws NoSuchAlgorithmException + * The provider does not support the specified algorithm. + */ + public Digest(String algorithm, Provider provider) throws NoSuchAlgorithmException + { + this(MessageDigest.getInstance(algorithm, provider)); + } + + + /** + * Create a {@code Digest} instance with the specified algorithm. + * + *+ * This method creates a {@link MessageDigest} instance by + * {@link MessageDigest#getInstance(String)} and wraps it + * in a {@code Digest} instance. + *
+ * + * @param algorithm + * Algorithm name such as "MD5" and "SHA-1". + * + * @return + * A {@code Digest} instance that implements the specified algorithm. + * + * @throws NoSuchAlgorithmException + * No provider supports the specified algorithm. + */ + public static Digest getInstance(String algorithm) throws NoSuchAlgorithmException + { + return new Digest(algorithm); + } + + + /** + * Create a {@code Digest} instance with the specified algorithm. + * + *+ * This method creates a {@link MessageDigest} instance by + * {@link MessageDigest#getInstance(String, String)} and wraps it + * in a {@code Digest} instance. + *
+ * + * @param algorithm + * Algorithm name such as "MD5" and "SHA-1". + * + * @param provider + * Provider name. + * + * @return + * A {@code Digest} instance that implements the specified algorithm. + * + * @throws NoSuchAlgorithmException + * The provider does not support the specified algorithm. + * + * @throws NoSuchProviderException + * The specified provider is not registered in the security provider list. + */ + public static Digest getInstance(String algorithm, String provider) + throws NoSuchAlgorithmException, NoSuchProviderException + { + return new Digest(algorithm, provider); + } + + + /** + * Create a {@code Digest} instance with the specified algorithm. + * + *+ * This method creates a {@link MessageDigest} instance by + * {@link MessageDigest#getInstance(String, Provider)} and wraps it + * in a {@code Digest} instance. + *
+ * + * @param algorithm + * Algorithm name such as "MD5" and "SHA-1". + * + * @param provider + * Provider. + * + * @return + * A {@code Digest} instance that implements the specified algorithm. + * + * @throws NoSuchAlgorithmException + * The provider does not support the specified algorithm. + */ + public static Digest getInstance(String algorithm, Provider provider) throws NoSuchAlgorithmException + { + return new Digest(algorithm, provider); + } + + + /** + * Create a {@code Digest} instance with the specified algorithm. + * + *+ * This method exists just to ignore {@code NoSuchAlgorithmException}. + *
+ * + * @param algorithm + * Algorithm name such as "MD5" and "SHA-1". + * + * @return + * A {@code Digest} instance that implements the specified algorithm. + */ + private static Digest getInstancePredefined(String algorithm) + { + try + { + return getInstance(algorithm); + } + catch (NoSuchAlgorithmException e) + { + // This won't happen. + return null; + } + } + + + /** + * Create a {@code Digest} instance that implements MD2. + * + * @return + * A {@code Digest} instance that implements MD2. + */ + public static Digest getInstanceMD2() + { + return getInstancePredefined("MD2"); + } + + + /** + * Create a {@code Digest} instance that implements MD5. + * + * @return + * A {@code Digest} instance that implements MD5. + */ + public static Digest getInstanceMD5() + { + return getInstancePredefined("MD5"); + } + + + /** + * Create a {@code Digest} instance that implements SHA-1. + * + * @return + * A {@code Digest} instance that implements SHA-1. + */ + public static Digest getInstanceSHA1() + { + return getInstancePredefined("SHA-1"); + } + + + /** + * Create a {@code Digest} instance that implements SHA-256. + * + * @return + * A {@code Digest} instance that implements SHA-256. + */ + public static Digest getInstanceSHA256() + { + return getInstancePredefined("SHA-256"); + } + + + /** + * Create a {@code Digest} instance that implements SHA-384. + * + * @return + * A {@code Digest} instance that implements SHA-384. + */ + public static Digest getInstanceSHA384() + { + return getInstancePredefined("SHA-384"); + } + + + /** + * Create a {@code Digest} instance that implements SHA-512. + * + * @return + * A {@code Digest} instance that implements SHA-512. + */ + public static Digest getInstanceSHA512() + { + return getInstancePredefined("SHA-512"); + } + + + /** + * Get the algorithm name. + * + *+ * This method just calls {@link MessageDigest#getAlgorithm() + * getAlgorithm()} of the wrapped {@code MessageDigest} instance. + *
+ * + * @return + * Algorithm name. + */ + public String getAlgorithm() + { + return mMessageDigest.getAlgorithm(); + } + + + /** + * Get the length of the digest in bytes. + * + *+ * This method just calls {@link MessageDigest#getDigestLength() + * getDigestLength()} of the wrapped {@code MessageDigest} instance. + *
+ * + * @return + * Length of the digest in bytes. + */ + public int getDigestLength() + { + return mMessageDigest.getDigestLength(); + } + + + /** + * Get the provider. + * + *+ * This method just calls {@link MessageDigest#getProvider() + * getProvider()} of the wrapped {@code MessageDigest} instance. + *
+ * + * @return + * Provider. + */ + public Provider getProvider() + { + return mMessageDigest.getProvider(); + } + + + /** + * Get the wrapped {@code MessageDigest} instance. + * + * @return + * The {@code MessageDigest} instance that has been + * given to the constructor. + */ + public MessageDigest getWrappedMessageDigest() + { + return mMessageDigest; + } + + + /** + * Get a clone of this {@code Digest} instance. + * + * @return + * A cloned object. + * + * @throws CloneNotSupportedException + * The implementation does not support {@code clone} operation. + */ + @SuppressWarnings("unchecked") + @Override + public Object clone() throws CloneNotSupportedException + { + Digest cloned = (Digest)super.clone(); + + cloned.mMessageDigest = (MessageDigest)mMessageDigest.clone(); + cloned.mFeatures = (HashMap+ * This method just calls {@link MessageDigest#digest() digest()} + * method of the wrapped {@code MessageDigest} instance. + *
+ * + * @return + * The resulting hash value. + */ + public byte[] digest() + { + return mMessageDigest.digest(); + } + + + /** + * Perform the final update with the given byte array, and then + * complete the hash computation. The digest is reset after + * this call is made. + * + *+ * This method just calls {@link MessageDigest#digest(byte[]) + * digest(byte[])} method of the wrapped {@code MessageDigest} + * instance. + *
+ * + * @param input + * Byte array used for the last update. + * + * @return + * The resulting hash value. + */ + public byte[] digest(byte[] input) + { + return mMessageDigest.digest(input); + } + + + /** + * Complete the hash computation. The digest is reset after + * this call is made. + * + *+ * This method just calls {@link MessageDigest#digest(byte[],int,int) + * digest(byte[], int, int)} method of the wrapped {@code MessageDigest} + * instance. + *
+ * + * @param output + * Output buffer for the computed digest. + * + * @param offset + * Offset into the output buffer to begin storing the digest. + * + * @param length + * Number of bytes within the output buffer allotted for the digest. + * + * @return + * The resulting hash value. + * + * @throws DigestException + * The {@code digest} method of the {@link MessageDigest} class + * failed. + */ + public int digest(byte[] output, int offset, int length) throws DigestException + { + return mMessageDigest.digest(output, offset, length); + } + + + /** + * Complete the hash computation and get the resulting hash value + * as a hex string. The digest is reset after this call is made. + * + *+ * This method calls {@link #digest()} method and converts the result + * to a String object. + *
+ * + * @return + * The result hash value represented in a hex String. + */ + public String digestAsString() + { + return bytesToHex(digest()); + } + + + /** + * Perform the final update with the given byte array, and then + * complete the hash computation and get the resulting hash value + * as a hex string. The digest is reset after this call is made. + * + *+ * This method calls {@link #digest(byte[])} method and converts + * the result to a String object. + *
+ * + * @param input + * Byte array used for the last update. + * + * @return + * The result hash value represented in a hex String. + */ + public String digestAsString(byte[] input) + { + return bytesToHex(digest(input)); + } + + + /** + * Complete the hash computation and get the resulting hash value + * as a string. The given encoder is used to convert the digest + * value to a string. + * + *+ * This method is an alias of {@link #digestAsString(byte[], + * BinaryEncoder) digestAsString((byte[])null, encoder)}. + *
+ * + * @param encoder + * Encoder to convert a digest value to a byte array + * whose elements are printable characters. For example, + * {@link org.apache.commons.codec.binary.Base64}. + * + * @return + * The result hash value encoded by the encoder. + * + * @throws RuntimeException + * If the encoder throws {@link EncoderException}, + * a {@code RuntimeException} wrapping the + * {@code EncoderException} is thrown. + */ + public String digestAsString(BinaryEncoder encoder) + { + return digestAsString((byte[])null, encoder); + } + + + /** + * Perform the final update with the given byte array, and then + * complete the hash computation and get the resulting hash value + * as a string. The given encoder is used to convert the digest + * value to a string. + * + * @param input + * Byte array used for the last update. If {@code null} + * is given, it is just ignored. + * + * @param encoder + * Encoder to convert a digest value to a byte array + * whose elements are printable characters. For example, + * {@link org.apache.commons.codec.binary.Base64}. + * + * @return + * The result hash value encoded by the encoder. + * + * @throws IllegalArgumentException + * {@code encoder} is {@code null}. + * + * @throws RuntimeException + * If the encoder throws {@link EncoderException}, + * a {@code RuntimeException} wrapping the + * {@code EncoderException} is thrown. + */ + public String digestAsString(byte[] input, BinaryEncoder encoder) + { + if (encoder == null) + { + throw new IllegalArgumentException("encoder is null."); + } + + // Compute the digest value. + byte[] digest = (input != null) ? digest(input) : digest(); + + // Encoded value. + byte[] encoded = null; + + try + { + // Encode the digest value. + encoded = encoder.encode(digest); + } + catch (EncoderException e) + { + // Failed to encode the digest value. + throw new RuntimeException("Failed to encode the digest value.", e); + } + + try + { + // Convert the byte array into a string. + return new String(encoded, "UTF-8"); + } + catch (UnsupportedEncodingException e) + { + // This won't happen. + return null; + } + } + + + /** + * Reset the wrapped {@code MessageDigest} instance. + * + * @return + * {@code this} object. + */ + public Digest reset() + { + mMessageDigest.reset(); + + return this; + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method calls {@link MessageDigest#update(byte) update(byte)} + * method of the wrapped {@code MessageDigest} instance. + *
+ * + * @param input + * Input data. + * + * @return + * {@code this} object. + */ + public Digest update(byte input) + { + mMessageDigest.update(input); + + return this; + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method calls {@link MessageDigest#update(byte[]) update(byte[])} + * method of the wrapped {@code MessageDigest} instance. + *
+ * + * @param input + * Input data. + * + * @return + * {@code this} object. + */ + public Digest update(byte[] input) + { + mMessageDigest.update(input); + + return this; + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method calls {@link MessageDigest#update(byte[],int,int) + * update(byte[], int, int)} method of the wrapped + * {@code MessageDigest} instance. + *
+ * + * @param input + * Input data. + * + * @param offset + * The offset to start from in the array. + * + * @param length + * The number of elements to use, starting at offset. + * + * @return + * {@code this} object. + */ + public Digest update(byte[] input, int offset, int length) + { + mMessageDigest.update(input, offset, length); + + return this; + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method calls {@link MessageDigest#update(ByteBuffer) + * update(ByteBuffer)} method of the wrapped {@code MessageDigest} + * instance. + *
+ * + * @param input + * Input data. + * + * @return + * {@code this} object. + */ + public Digest update(ByteBuffer input) + { + mMessageDigest.update(input); + + return this; + } + + + /** + * Check the validity of the combination of the given parameters. + * + * @param size + * Size of an array. + * + * @param offset + * Offset in the array. + * + * @param length + * Length of data to use. + * + * @throws IllegalArgumentException + *+ * This method is an alias of {@link #update(boolean) + * update}{@code (input.booleanValue())}. + *
+ * + * @param input + * Input data. + * + * @return + * {@code this} object. + */ + public Digest update(Boolean input) + { + if (input == null) + { + return this; + } + + return update(input.booleanValue()); + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method is an alias of {@link #update(Boolean[], int, int) + * update}{@code (input, 0, input.length)}. + *
+ * + * @param input + * Input data. If null is given, update is not performed. + * + * @return + * {@code this} object. + */ + public Digest update(Boolean[] input) + { + if (input == null) + { + return this; + } + + return update(input, 0, input.length); + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method calls {@link #update(Boolean)} for each + * array element which is in the specified range. + *
+ * + * @param input + * Input data. + * + * @param offset + * The offset to start from in the array. + * + * @param length + * The number of elements to use, starting at offset. + * + * @return + * {@code this} object. + * + * @throws IllegalArgumentException + * The range specified by the parameters is invalid. + */ + public Digest update(Boolean[] input, int offset, int length) + { + if (input == null) + { + return this; + } + + checkRange(input.length, offset, length); + + for (int i = 0; i < length; ++i) + { + update(input[i + offset]); + } + + return this; + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + * @param input + * Input data. {@code true} results in {@link #update(byte) + * update}{@code ((byte)1)} and {@code false} results in + * {@link #update(byte) update}{@code ((byte)0)}. + * + * @return + * {@code this} object. + */ + public Digest update(boolean input) + { + return update((byte)(input ? 1 : 0)); + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method is an alias of {@link #update(boolean[], int, int) + * update}{@code (input, 0, input.length)}. + *
+ * + * @param input + * Input data. If null is given, update is not performed. + * + * @return + * {@code this} object. + */ + public Digest update(boolean[] input) + { + if (input == null) + { + return this; + } + + return update(input, 0, input.length); + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method calls {@link #update(boolean)} for each + * array element which is in the specified range. + *
+ * + * @param input + * Input data. + * + * @param offset + * The offset to start from in the array. + * + * @param length + * The number of elements to use, starting at offset. + * + * @return + * {@code this} object. + * + * @throws IllegalArgumentException + * The range specified by the parameters is invalid. + */ + public Digest update(boolean[] input, int offset, int length) + { + if (input == null) + { + return this; + } + + checkRange(input.length, offset, length); + + for (int i = 0; i < length; ++i) + { + update(input[i + offset]); + } + + return this; + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method is an alias of {@link #update(char) + * update}{@code (input.charValue())}. + *
+ * + * @param input + * Input data. + * + * @return + * {@code this} object. + */ + public Digest update(Character input) + { + if (input == null) + { + return this; + } + + return update(input.charValue()); + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method is an alias of {@link #update(Character[], int, int) + * update}{@code (input, 0, input.length)}. + *
+ * + * @param input + * Input data. If null is given, update is not performed. + * + * @return + * {@code this} object. + */ + public Digest update(Character[] input) + { + if (input == null) + { + return this; + } + + return update(input, 0, input.length); + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method calls {@link #update(Character)} for each + * array element which is in the specified range. + *
+ * + * @param input + * Input data. + * + * @param offset + * The offset to start from in the array. + * + * @param length + * The number of elements to use, starting at offset. + * + * @return + * {@code this} object. + * + * @throws IllegalArgumentException + * The range specified by the parameters is invalid. + */ + public Digest update(Character[] input, int offset, int length) + { + if (input == null) + { + return this; + } + + checkRange(input.length, offset, length); + + for (int i = 0; i < length; ++i) + { + update(input[i + offset]); + } + + return this; + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method calls {@link #update(byte) update}{@code + * ((byte)((input >> 8) & 0xff))} and {@link #update(byte) + * update}({@code ((byte)(input >> 0) & 0xff)}. + *
+ * + * @param input + * Input data. + * + * @return + * {@code this} object. + */ + public Digest update(char input) + { + update((byte)((input >> 8) & 0xff)); + update((byte)((input >> 0) & 0xff)); + + return this; + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method is an alias of {@link #update(char[], int, int) + * update}{@code (input, 0, input.length)}. + *
+ * + * @param input + * Input data. If null is given, update is not performed. + * + * @return + * {@code this} object. + */ + public Digest update(char[] input) + { + if (input == null) + { + return this; + } + + return update(input, 0, input.length); + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method calls {@link #update(char)} for each + * array element which is in the specified range. + *
+ * + * @param input + * Input data. + * + * @param offset + * The offset to start from in the array. + * + * @param length + * The number of elements to use, starting at offset. + * + * @return + * {@code this} object. + * + * @throws IllegalArgumentException + * The range specified by the parameters is invalid. + */ + public Digest update(char[] input, int offset, int length) + { + if (input == null) + { + return this; + } + + checkRange(input.length, offset, length); + + for (int i = 0; i < length; ++i) + { + update(input[i + offset]); + } + + return this; + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method calls {@link #update(byte) update}{@code + * ((byte)((input >> 8) & 0xff))} and {@link #update(byte) + * update}({@code ((byte)(input >> 0) & 0xff)}. + *
+ * + * @param input + * Input data. + * + * @return + * {@code this} object. + */ + public Digest update(short input) + { + update((byte)((input >> 8) & 0xff)); + update((byte)((input >> 0) & 0xff)); + + return this; + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method is an alias of {@link #update(short[], int, int) + * update}{@code (input, 0, input.length)}. + *
+ * + * @param input + * Input data. If null is given, update is not performed. + * + * @return + * {@code this} object. + */ + public Digest update(short[] input) + { + if (input == null) + { + return this; + } + + return update(input, 0, input.length); + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method calls {@link #update(short)} for each + * array element which is in the specified range. + *
+ * + * @param input + * Input data. + * + * @param offset + * The offset to start from in the array. + * + * @param length + * The number of elements to use, starting at offset. + * + * @return + * {@code this} object. + * + * @throws IllegalArgumentException + * The range specified by the parameters is invalid. + */ + public Digest update(short[] input, int offset, int length) + { + if (input == null) + { + return this; + } + + checkRange(input.length, offset, length); + + for (int i = 0; i < length; ++i) + { + update(input[i + offset]); + } + + return this; + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method calls {@link #update(byte)} for each byte of the + * 4 bytes from MSB to LSB (from {@code ((input >> 24) & 0xff)} + * to {@code ((input >> 0) & 0xff)}). + *
+ * + * @param input + * Input data. + * + * @return + * {@code this} object. + */ + public Digest update(int input) + { + update((byte)((input >> 24) & 0xff)); + update((byte)((input >> 16) & 0xff)); + update((byte)((input >> 8) & 0xff)); + update((byte)((input >> 0) & 0xff)); + + return this; + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method is an alias of {@link #update(int[], int, int) + * update}{@code (input, 0, input.length)}. + *
+ * + * @param input + * Input data. If null is given, update is not performed. + * + * @return + * {@code this} object. + */ + public Digest update(int[] input) + { + if (input == null) + { + return this; + } + + return update(input, 0, input.length); + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method calls {@link #update(int)} for each + * array element which is in the specified range. + *
+ * + * @param input + * Input data. + * + * @param offset + * The offset to start from in the array. + * + * @param length + * The number of elements to use, starting at offset. + * + * @return + * {@code this} object. + * + * @throws IllegalArgumentException + * The range specified by the parameters is invalid. + */ + public Digest update(int[] input, int offset, int length) + { + if (input == null) + { + return this; + } + + checkRange(input.length, offset, length); + + for (int i = 0; i < length; ++i) + { + update(input[i + offset]); + } + + return this; + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method calls {@link #update(byte)} for each byte of the + * 8 bytes from MSB to LSB (from {@code ((input >> 54) & 0xff)} + * to {@code ((input >> 0) & 0xff)}). + *
+ * + * @param input + * Input data. + * + * @return + * {@code this} object. + */ + public Digest update(long input) + { + update((byte)((input >> 54) & 0xff)); + update((byte)((input >> 48) & 0xff)); + update((byte)((input >> 40) & 0xff)); + update((byte)((input >> 32) & 0xff)); + update((byte)((input >> 24) & 0xff)); + update((byte)((input >> 16) & 0xff)); + update((byte)((input >> 8) & 0xff)); + update((byte)((input >> 0) & 0xff)); + + return this; + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method is an alias of {@link #update(long[], int, int) + * update}{@code (input, 0, input.length)}. + *
+ * + * @param input + * Input data. If null is given, update is not performed. + * + * @return + * {@code this} object. + */ + public Digest update(long[] input) + { + if (input == null) + { + return this; + } + + return update(input, 0, input.length); + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method calls {@link #update(long)} for each + * array element which is in the specified range. + *
+ * + * @param input + * Input data. + * + * @param offset + * The offset to start from in the array. + * + * @param length + * The number of elements to use, starting at offset. + * + * @return + * {@code this} object. + * + * @throws IllegalArgumentException + * The range specified by the parameters is invalid. + */ + public Digest update(long[] input, int offset, int length) + { + if (input == null) + { + return this; + } + + checkRange(input.length, offset, length); + + for (int i = 0; i < length; ++i) + { + update(input[i + offset]); + } + + return this; + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method converts the given {@code float} value to a + * {@code int} by {@link Float#floatToRawIntBits(float)} + * and then passes it to {@link #update(int)}. + *
+ * + * @param input + * Input data. + * + * @return + * {@code this} object. + */ + public Digest update(float input) + { + return update(Float.floatToRawIntBits(input)); + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method is an alias of {@link #update(float[], int, int) + * update}{@code (input, 0, input.length)}. + *
+ * + * @param input + * Input data. If null is given, update is not performed. + * + * @return + * {@code this} object. + */ + public Digest update(float[] input) + { + if (input == null) + { + return this; + } + + return update(input, 0, input.length); + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method calls {@link #update(float)} for each + * array element which is in the specified range. + *
+ * + * @param input + * Input data. + * + * @param offset + * The offset to start from in the array. + * + * @param length + * The number of elements to use, starting at offset. + * + * @return + * {@code this} object. + * + * @throws IllegalArgumentException + * The range specified by the parameters is invalid. + */ + public Digest update(float[] input, int offset, int length) + { + if (input == null) + { + return this; + } + + checkRange(input.length, offset, length); + + for (int i = 0; i < length; ++i) + { + update(input[i + offset]); + } + + return this; + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method converts the given {@code double} value to a + * {@code long} by {@link Double#doubleToRawLongBits(double)} + * and then passes it to {@link #update(long)}. + *
+ * + * @param input + * Input data. + * + * @return + * {@code this} object. + */ + public Digest update(double input) + { + return update(Double.doubleToRawLongBits(input)); + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method is an alias of {@link #update(double[], int, int) + * update}{@code (input, 0, input.length)}. + *
+ * + * @param input + * Input data. If null is given, update is not performed. + * + * @return + * {@code this} object. + */ + public Digest update(double[] input) + { + if (input == null) + { + return this; + } + + return update(input, 0, input.length); + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method calls {@link #update(double)} for each + * array element which is in the specified range. + *
+ * + * @param input + * Input data. + * + * @param offset + * The offset to start from in the array. + * + * @param length + * The number of elements to use, starting at offset. + * + * @return + * {@code this} object. + * + * @throws IllegalArgumentException + * The range specified by the parameters is invalid. + */ + public Digest update(double[] input, int offset, int length) + { + if (input == null) + { + return this; + } + + checkRange(input.length, offset, length); + + for (int i = 0; i < length; ++i) + { + update(input[i + offset]); + } + + return this; + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method checks the class of the given instance and + * calls a corresponding {@code update} method. + *
+ * + *| Class | + *Executed code | + *
|---|---|
| {@code Byte} | + *{@link #update(byte) update}{@code (((Byte)number).byteValue())} | + *
| {@code Short} | + *{@link #update(short) update}{@code (((Short)number).shortValue())} | + *
| {@code Integer} | + *{@link #update(int) update}{@code (((Integer)number).intValue())} | + *
| {@code Long} | + *{@link #update(long) update}{@code (((Long)number).longValue())} | + *
| {@code Float} | + *{@link #update(float) update}{@code (((Float)number).floatValue())} | + *
| {@code Double} | + *{@link #update(double) update}{@code (((Double)number).doubleValue())} | + *
| {@code BigInteger} | + *{@link #update(byte[]) update}{@code (((BigInteger)number).toByteArray())} | + *
| {@code BigDecimal} | + *{@link #update(String) update}{@code (((BigDecimal)number).toString())} | + *
| {@code AtomicInteger} | + *{@link #update(int) update}{@code (((AtomicInteger)number).intValue())} | + *
| {@code AtomicLong} | + *{@link #update(long) update}{@code (((AtomicLong)number).longValue())} | + *
| Others | + *Ignored. | + *
+ * This method is an alias of {@link #update(Number[], int, int) + * update}{@code (input, 0, input.length)}. + *
+ * + * @param+ * This method calls {@link #update(Number)} for each + * array element which is in the specified range. + *
+ * + * @param+ * This method converts the given string into bytes with the + * character set of UTF-8 and then passes the byte array to + * {@link #update(byte[])}. + *
+ * + * @param input + * Input data. If null is given, update is not performed. + * + * @return + * {@code this} object. + */ + public Digest update(String input) + { + if (input == null) + { + return this; + } + + try + { + return update(input.getBytes("UTF-8")); + } + catch (UnsupportedEncodingException e) + { + // This won't happen. + return this; + } + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method is an alias of {@link #update(String[], int, int) + * update}{@code (input, 0, input.length)}. + *
+ * + * @param input + * Input data. If null is given, update is not performed. + * + * @return + * {@code this} object. + */ + public Digest update(String[] input) + { + if (input == null) + { + return this; + } + + return update(input, 0, input.length); + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method calls {@link #update(String)} for each + * array element which is in the specified range. + *
+ * + * @param input + * Input data. + * + * @param offset + * The offset to start from in the array. + * + * @param length + * The number of elements to use, starting at offset. + * + * @return + * {@code this} object. + * + * @throws IllegalArgumentException + * The range specified by the parameters is invalid. + */ + public Digest update(String[] input, int offset, int length) + { + if (input == null) + { + return this; + } + + checkRange(input.length, offset, length); + + for (int i = 0; i < length; ++i) + { + update(input[i + offset]); + } + + return this; + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method calls {@link #update(Object)} for each element. + *
+ * + * @param input + * Input data. If {@code null} is given, update is not performed. + * {@code null} elements are ignored. Elements of unsupported + * classes are ignored, too. + * + * @return + * {@code this} object. + */ + public Digest update(Iterable> input) + { + if (input == null) + { + return this; + } + + for (Object element : input) + { + if (element == null) + { + continue; + } + + update(element); + } + + return this; + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method calls {@link #update(Object)} for each element. + *
+ * + * @param input + * Input data. If {@code null} is given, update is not performed. + * {@code null} elements are ignored. Elements of unsupported + * classes are ignored, too. + * + * @return + * {@code this} object. + */ + public Digest update(Object... input) + { + if (input == null) + { + return this; + } + + for (Object element : input) + { + if (element == null) + { + continue; + } + + update(element); + } + + return this; + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given input data. + * + *+ * This method checks the class of the given object and calls + * a corresponding {@code update} method. + *
+ * + * @param input + * Input data. If {@code null} is given, update is not performed. + * + * @return + * {@code this} object. + */ + public Digest update(Object input) + { + if (input == null) + { + return this; + } + + if (input instanceof String) + { + update((String)input); + } + else if (input instanceof String[]) + { + update((String[])input); + } + else if (input instanceof Character) + { + update((Character)input); + } + else if (input instanceof Character[]) + { + update((Character[])input); + } + else if (input instanceof Boolean) + { + update((Boolean)input); + } + else if (input instanceof boolean[]) + { + update((boolean[])input); + } + else if (input instanceof Boolean[]) + { + update((Boolean[])input); + } + else if (input instanceof Number) + { + update((Number)input); + } + else if (input instanceof byte[]) + { + update((byte[])input); + } + else if (input instanceof Byte[]) + { + update((Byte[])input); + } + else if (input instanceof ByteBuffer) + { + update((ByteBuffer)input); + } + else if (input instanceof char[]) + { + update((char[])input); + } + else if (input instanceof double[]) + { + update((double[])input); + } + else if (input instanceof Double[]) + { + update((Double[])input); + } + else if (input instanceof float[]) + { + update((float[])input); + } + else if (input instanceof Float[]) + { + update((Float[])input); + } + else if (input instanceof int[]) + { + update((int[])input); + } + else if (input instanceof Integer[]) + { + update((Integer[])input); + } + else if (input instanceof long[]) + { + update((long[])input); + } + else if (input instanceof Long[]) + { + update((Long[])input); + } + else if (input instanceof short[]) + { + update((short[])input); + } + else if (input instanceof Short[]) + { + update((Short[])input); + } + else if (input instanceof Iterable>) + { + update((Iterable>)input); + } + + return this; + } + + + /** + * Update the wrapped {@code MessageDigest} object with the + * given JSON. This method updates the digest based on the + * content of the given JSON, and in the respect, this method + * is different from {@link #update(String)}. + * + *+ * JSONs with the same content, for example, two JSONs below, + * generate the same digest. + *
+ * + *
+ * { "key1":"value1", "key2":"value2" }
+ * { "key1" : "value1" , "key2" : "value2" }
+ *
+ *
+ * + * If {@link Feature#IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_NULL} + * is enabled (it is disabled by default), key-value entries + * with value 'null' are treated as if they did not exist. + * Therefore, two JSONs below generate the same digest value. + *
+ * + *
+ * { "key1":"value1", "key2":null }
+ * { "key1":"value1" }
+ *
+ *
+ * + * If {@link Feature#SORT_JSON_OBJECT_ENTRY_KEYS} is enabled + * (it is enabled by default), orders of JSON object keys do + * not matter. Therefore, two JSONs below generate the same + * digest value. + *
+ * + *
+ * { "key1":"value1", "key2":"value2" }
+ * { "key2":"value2", "key1":"value1" }
+ *
+ *
+ * @param json
+ * JSON.
+ *
+ * @return
+ * {@code this} object.
+ *
+ * @throws IOException
+ * Failed to parse the given JSON.
+ */
+ public Digest updateJson(String json) throws IOException
+ {
+ return new JsonDigestUpdater().update(this, json);
+ }
+
+
+ /**
+ * Check if the specified feature is enabled.
+ *
+ * @param feature
+ * Feature to check.
+ *
+ * @return
+ * {@code true} if the feature is enabled. Otherwise, {@code false}.
+ */
+ public boolean isEnabled(Feature feature)
+ {
+ return mFeatures.get(feature).booleanValue();
+ }
+
+
+ /**
+ * Enable or disable the specified feature.
+ *
+ * @param feature
+ * {@link Feature} to enable or disable.
+ *
+ * @param enabled
+ * {@code true} to enable the feature.
+ * {@code false} to disable the feature.
+ *
+ * @return
+ * {@code this} object.
+ */
+ public Digest setEnabled(Feature feature, boolean enabled)
+ {
+ mFeatures.put(feature, Boolean.valueOf(enabled));
+
+ return this;
+ }
+
+
+ /**
+ * Convert the given byte array to a hex string.
+ *
+ * @param bytes
+ * A byte array to convert.
+ *
+ * @return
+ * A hex string with 0-9 and a-f.
+ */
+ public static String bytesToHex(byte[] bytes)
+ {
+ // http://stackoverflow.com/a/9855338/1174054
+ char[] hexChars = new char[bytes.length * 2];
+ int v;
+
+ for (int j = 0; j < bytes.length; ++j)
+ {
+ v = bytes[j] & 0xFF;
+ hexChars[j * 2] = mHexArray[v >>> 4];
+ hexChars[j * 2 + 1] = mHexArray[v & 0x0F];
+ }
+
+ return new String(hexChars);
+ }
+}
diff --git a/src/main/java/com/authlete/common/util/digest/JsonDigestUpdater.java b/src/main/java/com/authlete/common/util/digest/JsonDigestUpdater.java
new file mode 100644
index 0000000000..5026a2e54e
--- /dev/null
+++ b/src/main/java/com/authlete/common/util/digest/JsonDigestUpdater.java
@@ -0,0 +1,398 @@
+package com.authlete.common.util.digest;
+
+
+import static com.authlete.common.util.digest.Digest.Feature.IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_EMPTY_ARRAY;
+import static com.authlete.common.util.digest.Digest.Feature.IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_EMPTY_OBJECT;
+import static com.authlete.common.util.digest.Digest.Feature.IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_EMPTY_STRING;
+import static com.authlete.common.util.digest.Digest.Feature.IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_FALSE;
+import static com.authlete.common.util.digest.Digest.Feature.IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_NULL;
+import static com.authlete.common.util.digest.Digest.Feature.IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_ZERO;
+import static com.authlete.common.util.digest.Digest.Feature.SORT_JSON_OBJECT_ENTRY_KEYS;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+
+/**
+ * Implementation of {@link Digest#updateJson(String)}.
+ *
+ * @author Takahiko Kawasaki
+ *
+ * @since 4.23
+ */
+class JsonDigestUpdater
+{
+ private Digest digest;
+ private StringBuilder path;
+ private boolean ignoreNull;
+ private boolean ignoreFalse;
+ private boolean ignoreZero;
+ private boolean ignoreEmptyString;
+ private boolean ignoreEmptyArray;
+ private boolean ignoreEmptyObject;
+ private boolean sortKeys;
+
+
+ public Digest update(Digest digest, String json) throws IOException
+ {
+ // Set the digest to update.
+ this.digest = digest;
+
+ // Initialize the path.
+ this.path = new StringBuilder();
+
+ // Copy configuration.
+ this.ignoreNull = digest.isEnabled(IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_NULL);
+ this.ignoreFalse = digest.isEnabled(IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_FALSE);
+ this.ignoreZero = digest.isEnabled(IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_ZERO);
+ this.ignoreEmptyString = digest.isEnabled(IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_EMPTY_STRING);
+ this.ignoreEmptyArray = digest.isEnabled(IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_EMPTY_ARRAY);
+ this.ignoreEmptyObject = digest.isEnabled(IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_EMPTY_OBJECT);
+ this.sortKeys = digest.isEnabled(SORT_JSON_OBJECT_ENTRY_KEYS);
+
+ // Convert JSON to a node tree.
+ JsonNode root = createTree(json);
+
+ // Path start
+ mark("P");
+
+ // Traverse the node tree.
+ update(root);
+
+ // Path end
+ mark("p");
+
+ // Finally, update the digest with 'path' in order to prevent
+ // different JSONs from generating the same digest accidentally.
+ digest.update(path.toString());
+
+ return digest;
+ }
+
+
+ private JsonNode createTree(String json) throws IOException
+ {
+ return createObjectMapper().readTree(json);
+ }
+
+
+ private ObjectMapper createObjectMapper()
+ {
+ ObjectMapper mapper = new ObjectMapper();
+
+ mapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true);
+
+ return mapper;
+ }
+
+
+ private void mark(String mark)
+ {
+ path.append(mark);
+ }
+
+
+ private void update(JsonNode node)
+ {
+ if (node.isArray())
+ {
+ updateArray(node);
+ }
+ else if (node.isObject())
+ {
+ updateObject(node);
+ }
+ else if (node.isBoolean())
+ {
+ updateBoolean(node);
+ }
+ else if (node.isNumber())
+ {
+ updateNumber(node);
+ }
+ else if (node.isTextual())
+ {
+ updateText(node);
+ }
+ else if (node.isNull())
+ {
+ updateNull(node);
+ }
+ else if (node.isBinary())
+ {
+ // Probably, this won't happen.
+ updateBinary(node);
+ }
+ }
+
+
+ private void updateArray(JsonNode node)
+ {
+ // Array start.
+ mark("A");
+
+ // For each element.
+ for (JsonNode element : node)
+ {
+ // Array element start.
+ mark("E");
+
+ // Update the digest with the element.
+ update(element);
+
+ // Array element end.
+ mark("e");
+ }
+
+ // Array end.
+ mark("a");
+ }
+
+
+ private void updateObject(JsonNode node)
+ {
+ // Object start.
+ mark("O");
+
+ // Field names.
+ List+ * // Create a cipher with a secret key. + * AESCipher cipher = new {@link #AESCipher()}.{@link #setKey(String, String) setKey}("secret key", "initial vector"); + * + * // Encryption & decryption. + * // 'plaintext' and 'decrypted' have the same value. + * String plaintext = "plain text"; + * String encrypted = cipher.{@link #encrypt(String) encrypt(plaintext)}; + * String decrypted = cipher.{@link #decrypt(String) decrypt(encrypted)}; + * + * // In the above example, 'encrypted' is encoded by Base64 (default). + * // If you want to change the format, use {@code setCoder} method. + * // For example, to change the format to hexadecimal: + * Hex hex = new org.apache.commons.codec.binary.Hex(); + * cipher.setCoder(hex); + * + * // Binary representation (only "0"s and "1"s) also can be used. + * BinaryCodec binary = new org.apache.commons.codec.BinaryCodec(); + * cipher.setCoder(binary); + * + * // Coder can be specified as a constructor parameter. + * cipher = new AESCipher(hex); + * + * // If you want, an encoder and a decoder can be set separately. + * cipher.{@link #setEncoder(BinaryEncoder) setEncoder(hex)}; + * cipher.{@link #setDecoder(BinaryDecoder) setDecoder(hex)}; + *+ * + *
+ * // Another example which performs encryption without initial vector. + * String secretkey = "secret key"; + * String plaintext = "plain text"; + * + * // Create and set up without initial vector. + * AESCipher cipher = new AESCipher().setKey(secretkey); + * + * // Encrypt. + * String encrypted = cipher.encrypt(plaintext); + * + * // Get the auto-generated initial vector. + * byte[] iv = cipher.getCipher().getIV(); + * + * // Decryption requires initial vector. + * cipher.setKey(secretkey, iv); + * + * // Decrypt. + * String decrypted = cipher.decrypt(encrypted); + *+ * + * @author Takahiko Kawasaki + * @since 4.23 + */ +public class AESCipher extends CodecCipher +{ + /** + * The default key size. The value is 16, meaning 128 bits. + */ + public static final int DEFAULT_KEY_SIZE = 16; + + + /** + * The default transformation. The value is "AES/CBC/PKCS5Padding". + */ + public static final String DEFAULT_TRANSFORMATION = AES_CBC_PKCS5PADDING; + + + /** + * The initial vector. The size of initial vectors for AES is always 16. + */ + private static final int INITIAL_VECTOR_SIZE = 16; + + + /** + * Constructor. + * + *
+ * This constructor just performs {@link CodecCipher#CodecCipher(String) + * super("AES/CBC/PKCS5Padding")}. + *
+ */ + public AESCipher() + { + super(DEFAULT_TRANSFORMATION); + } + + + /** + * Constructor. + * + *+ * This constructor just performs {@link CodecCipher#CodecCipher(String) + * super(transformation)}. + *
+ */ + public AESCipher(String transformation) + { + super(transformation); + } + + + /** + * Constructor with an encoder and a decoder. + * + *+ * This constructor just performs {@link CodecCipher#CodecCipher(String, + * BinaryEncoder, BinaryDecoder) super("AES/CBC/PKCS5Padding", encoder, decoder)}. + *
+ * + * @param encoder + * An encoder used in {@link #encrypt(String) encrypt(String)} and + * {@link #encrypt(byte[]) encrypt(byte[])} to encode an encrypted byte array. + * If {@code null} is given, {@link Base64} is used as the default + * encoder. + * + * @param decoder + * A decoder used in {@link #decrypt(String) decrypt(String)} and + * {@link #decrypt(byte[]) decrypt(byte[])} to decode an encoded input byte array. + * If {@code null} is given, {@link Base64} is used as the default + * decoder. + */ + public AESCipher(BinaryEncoder encoder, BinaryDecoder decoder) + { + super(DEFAULT_TRANSFORMATION, encoder, decoder); + } + + + /** + * Constructor with a transformation, an encoder and a decoder. + * + *+ * This constructor just performs {@link CodecCipher#CodecCipher(String, + * BinaryEncoder, BinaryDecoder) super(transformation, encoder, decoder)}. + *
+ * + * @param encoder + * An encoder used in {@link #encrypt(String) encrypt(String)} and + * {@link #encrypt(byte[]) encrypt(byte[])} to encode an encrypted byte array. + * If {@code null} is given, {@link Base64} is used as the default + * encoder. + * + * @param decoder + * A decoder used in {@link #decrypt(String) decrypt(String)} and + * {@link #decrypt(byte[]) decrypt(byte[])} to decode an encoded input byte array. + * If {@code null} is given, {@link Base64} is used as the default + * decoder. + */ + public AESCipher(String transformation, BinaryEncoder encoder, BinaryDecoder decoder) + { + super(transformation, encoder, decoder); + } + + + /** + * Constructor with a coder. + * + *+ * This constructor just performs {@code super("AES/CBC/PKCS5Padding", coder)}. + *
+ * + * @param coder + * A coder which works as both an encoder and a decoder. + * If {@code null} is given, {@link Base64} is used as the + * default coder. + */ + public+ * This constructor just performs {@code super(transformation, coder)}. + *
+ * + * @param coder + * A coder which works as both an encoder and a decoder. + * If {@code null} is given, {@link Base64} is used as the + * default coder. + */ + public+ * This method is an alias of {@link #setInit(java.security.Key, + * java.security.spec.AlgorithmParameterSpec) setInit(key, iv)}. + *
+ * + * @param key + * Secret key. + * + * @param iv + * Initial vector. + * + * @return + * {@code this} object. + * + * @throws IllegalArgumentException + * {@code key} is {@code null}. + */ + public AESCipher setKey(SecretKey key, IvParameterSpec iv) + { + return (AESCipher)setInit(key, iv); + } + + + /** + * Set cipher initialization parameters. + * + *
+ * This method is an alias of {@link #setKey(byte[], byte[], int)
+ * setKey}(key, iv, {@link #DEFAULT_KEY_SIZE}).
+ *
+ * This method constructs a {@link SecretKey} instance and an + * {@link IvParameterSpec} instance from the arguments, + * and then calls {@link #setKey(SecretKey, IvParameterSpec)}. + *
+ * + * @param key + * Secret key. If {@code null} is given, {@code new byte[16]} + * is used. If not {@code null} and the length is less than 16, + * a byte array of size 16 is allocated and the content of + * {@code key} is copied to the newly allocated byte array, + * and the resultant byte array is used. + * + * @param iv + * Initial vector. If {@code null} is given, {@code null} + * is used, meaning that {@code IvParameterSepc} argument + * passed to {@link #setKey(SecretKey, IvParameterSpec)} is + * {@code null}. In that case, you will want to obtain the + * auto-generated initial vector by calling {@link #getCipher() + * getCipher()}{@code .}{@link javax.crypto.Cipher#getIV() + * getIV()} in order to decrypt the encrypted data. + * + *+ * If {@code iv} is not {@code null} and the length is less + * than 16, a byte array of size 16 is allocated and the content + * of {@code iv} is copied to the newly allocated byte array, + * and the resultant byte array is used. Even if the length is + * greater than 16, only the first 16 bytes are used to construct + * an {@code IvParameterSpec} instance. + *
+ * + * @param keySize + * The size of the secret key in bytes. + * + * @return + * {@code this} object. + */ + public AESCipher setKey(byte[] key, byte[] iv, int keySize) + { + SecretKey secretKey = Utils.createSecretKeySpec(key, getAlgorithm(), keySize); + IvParameterSpec spec = null; + + if (iv != null) + { + spec = Utils.createIvParameterSpec(iv, INITIAL_VECTOR_SIZE); + } + + return setKey(secretKey, spec); + } + + + /** + * Set cipher initialization parameters. + * + *
+ * This method is an alias of {@link #setKey(String, byte[], int)
+ * setKey}(key, iv, {@link #DEFAULT_KEY_SIZE}).
+ *
+ * This method is an alias of {@link #setKey(byte[], byte[], int)}. + *
+ * + * @param key + * Secret key. The value is converted to a byte array + * by {@code key.getBytes("UTF-8")} and used as the + * first argument of {@link #setKey(byte[], byte[], int)}. + * + * @param iv + * Initial vector. + * + * @param keySize + * The size of the secret key in bytes. + * + * @return + * {@code this} object. + */ + public AESCipher setKey(String key, byte[] iv, int keySize) + { + byte[] key2 = Utils.getBytesUTF8(key); + + return setKey(key2, iv, keySize); + } + + + /** + * Set cipher initialization parameters. + * + *
+ * This method is an alias of {@link #setKey(String, String, int)
+ * setKey}(key, iv, {@link #DEFAULT_KEY_SIZE}).
+ *
+ * This method is an alias of {@link #setKey(byte[], byte[], int)}. + *
+ * + * @param key + * Secret key. The value is converted to a byte array + * by {@code key.getBytes("UTF-8")} and used as the + * first argument of {@link #setKey(byte[], byte[], int)}. + * + * @param iv + * Initial vector. The value is converted to a byte array + * by {@code iv.getBytes("UTF-8")} and used as the + * second argument of {@link #setKey(byte[], byte[], int)}. + * + * @param keySize + * The size of the secret key in bytes. + * + * @return + * {@code this} object. + */ + public AESCipher setKey(String key, String iv, int keySize) + { + byte[] key2 = Utils.getBytesUTF8(key); + byte[] iv2 = Utils.getBytesUTF8(iv); + + return setKey(key2, iv2, keySize); + } + + + /** + * Set cipher initialization parameters. + * + *
+ * This method is an alias of {@link #setKey(String, int)
+ * setKey}(key, {@link #DEFAULT_KEY_SIZE})
+ *
+ * This method is an alias of {@link #setKey(byte[], byte[], int)}. + *
+ * + * @param key + * Secret key. The value is converted to a byte array + * by {@code key.getBytes("UTF-8")} and used as the + * first argument of {@link #setKey(byte[], byte[], int)}. + * + * @param keySize + * The size of the secret key in bytes. + * + * @return + * {@code this} object. + */ + public AESCipher setKey(String key, int keySize) + { + byte[] key2 = Utils.getBytesUTF8(key); + + return setKey(key2, (byte[])null, keySize); + } + + + /** + * Set cipher initialization parameters. + * + *
+ * This method is an alias of {@link #setKey(byte[], String, int)
+ * setKey}(key, iv, {@link #DEFAULT_KEY_SIZE}).
+ *
+ * This method is an alias of {@link #setKey(byte[], byte[], int)}. + *
+ * + * @param key + * Secret key. + * + * @param iv + * Initial vector. The value is converted to a byte array + * by {@code iv.getBytes("UTF-8")} and used as the + * second argument of {@link #setKey(byte[], byte[], int)}. + * + * @param keySize + * The size of the secret key in bytes. + * + * @return + * {@code this} object. + */ + public AESCipher setKey(byte[] key, String iv, int keySize) + { + byte[] iv2 = Utils.getBytesUTF8(iv); + + return setKey(key, iv2, keySize); + } + + + /** + * Set cipher initialization parameters. + * + *
+ * This method is an alias of {@link #setKey(byte[], int)
+ * setKey}(key, {@link #DEFAULT_KEY_SIZE}).
+ *
+ * This method is an alias of {@link #setKey(byte[], byte[], int)
+ * setKey}(key, (byte[])null, keySize).
+ *
+ * // This example shows encryption/decryption using "AES/CBC/PKCS5Padding", + * // but {@link AESCipher AESCipher} is much easier to use for "AES/CBC/PKCS5Padding". + * + * // Prepare cipher initialization parameters. + * Key key + * = new SecretKeySpec("1234567890123456".getBytes("UTF-8"), "AES"); + * AlgorithmParameterSpec spec + * = new IvParameterSpec("abcdefghijklmnop".getBytes("UTF-8")); + * + * // Create a cipher using "AES/CBC/PKCS5Padding". + * String transformation = {@link StandardCipherTransformations}.{@link StandardCipherTransformations#AES_CBC_PKCS5PADDING AES_CBC_PKCS5PADDING}; + * CodecCipher cipher = new {@link #CodecCipher(String) CodecCipher(transformation)}; + * + * // Set initialization parameters. + * cipher.{@link #setInit(Key, AlgorithmParameterSpec) setInit(key, spec)}; + * + * // Encryption & decryption. + * // 'plaintext' and 'decrypted' have the same value. + * String plaintext = "plain text"; + * String encrypted = cipher.{@link #encrypt(String) encrypt(plaintext)}; + * String decrypted = cipher.{@link #decrypt(String) decrypt(encrypted)}; + * + * // In the above example, 'encrypted' is encoded by Base64 (default). + * // If you want to change the format, use {@code setCoder} method. + * // For example, to change the format to hexadecimal: + * Hex hex = new org.apache.commons.codec.binary.Hex(); + * cipher.setCoder(hex); + * + * // Binary representation (only "0"s and "1"s) also can be used. + * BinaryCodec binary = new org.apache.commons.codec.BinaryCodec(); + * cipher.setCoder(binary); + * + * // Coder can be specified as a parameter of some constructors. + * cipher = new CodecCipher("AES/CBC/PKCS5Padding", hex); + * + * // If you want, an encoder and a decoder can be set separately. + * cipher.{@link #setEncoder(BinaryEncoder) setEncoder(hex)}; + * cipher.{@link #setDecoder(BinaryDecoder) setDecoder(hex)}; + *+ * + * @see javax.crypto.Cipher + * + * @author Takahiko Kawasaki + * @since 4.23 + */ +public class CodecCipher +{ + private static final Base64 DEFAULT_CODER = new Base64(); + + private Cipher cipher; + private BinaryEncoder encoder; + private BinaryDecoder decoder; + private Key key; + private AlgorithmParameters params; + private AlgorithmParameterSpec spec; + private Certificate certificate; + private SecureRandom random; + + + /** + * The default constructor. + */ + public CodecCipher() + { + } + + + /** + * Constructor with a cipher, an encoder and a decoder. + * + * @param cipher + * A cipher. If {@code null} is given, {@link #setCipher(Cipher)} + * must be called later with a valid cipher. + * + * @param encoder + * An encoder used in {@link #encrypt(String)} and + * {@link #encrypt(byte[])} to encode an encrypted byte array. + * If {@code null} is given, {@link Base64} is used as the default + * encoder. + * + * @param decoder + * A decoder used in {@link #decrypt(String)} and + * {@link #decrypt(byte[])} to decode an encoded input byte array. + * If {@code null} is given, {@link Base64} is used as the default + * decoder. + */ + public CodecCipher(Cipher cipher, BinaryEncoder encoder, BinaryDecoder decoder) + { + this.cipher = cipher; + this.encoder = encoder; + this.decoder = decoder; + } + + + /** + * Constructor with a cipher and a coder. + * + *
+ * This constructor is an alias of {@link #CodecCipher(Cipher, BinaryEncoder, BinaryDecoder) + * CodecCipher(cipher, coder, coder)}. + *
+ * + * @param cipher + * A cipher. If {@code null} is given, {@link #setCipher(Cipher)} + * must be called later with a valid cipher. + * + * @param coder + * A coder which works as both an encoder and a decoder. + * If {@code null} is given, {@link Base64} is used as the + * default coder. + */ + public+ * This constructor is an alias of {@link #CodecCipher(Cipher, BinaryEncoder, BinaryDecoder) + * CodecCipher(cipher, (BinaryEncoder)null, (BinaryDecoder)null)}. + *
+ * + * @param cipher + * A cipher. If {@code null} is given, {@link #setCipher(Cipher)} + * must be called later with a valid cipher. + */ + public CodecCipher(Cipher cipher) + { + this(cipher, (BinaryEncoder)null, (BinaryDecoder)null); + } + + + /** + * Constructor with a transformation, an encoder and a decoder. + * + * @param transformation + * A transformation in the form of either"algorithm/mode/padding"
+ * or "algorithm". For example, "AES/CBC/PKCS5Padding".
+ *
+ * @param encoder
+ * An encoder used in {@link #encrypt(String)} and
+ * {@link #encrypt(byte[])} to encode an encrypted byte array.
+ * If {@code null} is given, {@link Base64} is used as the default
+ * encoder.
+ *
+ * @param decoder
+ * A decoder used in {@link #decrypt(String)} and
+ * {@link #decrypt(byte[])} to decode an encoded input byte array.
+ * If {@code null} is given, {@link Base64} is used as the default
+ * decoder.
+ *
+ * @throws IllegalArgumentException
+ * The given transformation is not supported. This exception wraps
+ * the original exception (such as {@link java.security.NoSuchAlgorithmException
+ * NoSuchAlgorithmException} and {@link javax.crypto.NoSuchPaddingException
+ * NoSuchPaddingException}) as the cause.
+ *
+ * @see Cipher (Encryption) Algorithms
+ *
+ * @see StandardCipherTransformations
+ */
+ public CodecCipher(String transformation, BinaryEncoder encoder, BinaryDecoder decoder) throws IllegalArgumentException
+ {
+ this(getCipherInstance(transformation), encoder, decoder);
+ }
+
+
+ /**
+ * Constructor with a transformation and a coder.
+ *
+ * + * This constructor is an alias of {@link #CodecCipher(String, BinaryEncoder, BinaryDecoder) + * CodecCipher(transformation, coder, coder)}. + *
+ * + * @param transformation + * A transformation in the form of either"algorithm/mode/padding"
+ * or "algorithm". For example, "AES/CBC/PKCS5Padding".
+ *
+ * @param coder
+ * A coder which works as both an encoder and a decoder.
+ * If {@code null} is given, {@link Base64} is used as the
+ * default coder.
+ *
+ * @throws IllegalArgumentException
+ * The given transformation is not supported. This exception wraps
+ * the original exception (such as {@link java.security.NoSuchAlgorithmException
+ * NoSuchAlgorithmException} and {@link javax.crypto.NoSuchPaddingException
+ * NoSuchPaddingException}) as the cause.
+ *
+ * @see Cipher (Encryption) Algorithms
+ *
+ * @see StandardCipherTransformations
+ */
+ public + * This constructor is an alias of {@link #CodecCipher(String, BinaryEncoder, BinaryDecoder) + * CodecCipher(transformation, (BinaryEncoder)null, (BinaryDecoder)null)}. + *
+ * + * @param transformation + * A transformation in the form of either"algorithm/mode/padding"
+ * or "algorithm". For example, "AES/CBC/PKCS5Padding".
+ *
+ * @throws IllegalArgumentException
+ * The given transformation is not supported. This exception wraps
+ * the original exception (such as {@link java.security.NoSuchAlgorithmException
+ * NoSuchAlgorithmException} and {@link javax.crypto.NoSuchPaddingException
+ * NoSuchPaddingException}) as the cause.
+ *
+ * @see Cipher (Encryption) Algorithms
+ *
+ * @see StandardCipherTransformations
+ */
+ public "algorithm/mode/padding"
+ * or "algorithm". For example, "AES/CBC/PKCS5Padding".
+ *
+ * @param provider
+ * A name of provider which provides the implementation of the
+ * transformation.
+ *
+ * @param encoder
+ * An encoder used in {@link #encrypt(String)} and
+ * {@link #encrypt(byte[])} to encode an encrypted byte array.
+ * If {@code null} is given, {@link Base64} is used as the default
+ * encoder.
+ *
+ * @param decoder
+ * A decoder used in {@link #decrypt(String)} and
+ * {@link #decrypt(byte[])} to decode an encoded input byte array.
+ * If {@code null} is given, {@link Base64} is used as the default
+ * decoder.
+ *
+ * @throws IllegalArgumentException
+ * The given transformation is not supported. This exception wraps
+ * the original exception (such as {@link java.security.NoSuchAlgorithmException
+ * NoSuchAlgorithmException}, {@link javax.crypto.NoSuchPaddingException
+ * NoSuchPaddingException} and {@link java.security.NoSuchProviderException
+ * NoSuchProviderException}) as the cause.
+ *
+ * @see Cipher (Encryption) Algorithms
+ *
+ * @see StandardCipherTransformations
+ */
+ public CodecCipher(String transformation, String provider, BinaryEncoder encoder, BinaryDecoder decoder) throws IllegalArgumentException
+ {
+ this(getCipherInstance(transformation, provider), encoder, decoder);
+ }
+
+
+ /**
+ * Constructor with a transformation, a provider and a coder.
+ *
+ * + * This constructor is an alias of {@link #CodecCipher(String, String, BinaryEncoder, BinaryDecoder) + * CodecCipher(transformation, provider, coder, coder)}. + *
+ * + * @param transformation + * A transformation in the form of either"algorithm/mode/padding"
+ * or "algorithm". For example, "AES/CBC/PKCS5Padding".
+ *
+ * @param provider
+ * A name of provider which provides the implementation of the
+ * transformation.
+ *
+ * @param coder
+ * A coder which works as both an encoder and a decoder.
+ * If {@code null} is given, {@link Base64} is used as the
+ * default coder.
+ *
+ * @throws IllegalArgumentException
+ * The given transformation is not supported. This exception wraps
+ * the original exception (such as {@link java.security.NoSuchAlgorithmException
+ * NoSuchAlgorithmException}, {@link javax.crypto.NoSuchPaddingException
+ * NoSuchPaddingException} and {@link java.security.NoSuchProviderException
+ * NoSuchProviderException}) as the cause.
+ *
+ * @see Cipher (Encryption) Algorithms
+ *
+ * @see StandardCipherTransformations
+ */
+ public + * This constructor is an alias of {@link #CodecCipher(String, String, BinaryEncoder, BinaryDecoder) + * CodecCipher(transformation, provider, (BinaryEncoder)null, (BinaryDecoder)null)}. + *
+ * + * @param transformation + * A transformation in the form of either"algorithm/mode/padding"
+ * or "algorithm". For example, "AES/CBC/PKCS5Padding".
+ *
+ * @param provider
+ * A name of provider which provides the implementation of the
+ * transformation.
+ *
+ * @throws IllegalArgumentException
+ * The given transformation is not supported. This exception wraps
+ * the original exception (such as {@link java.security.NoSuchAlgorithmException
+ * NoSuchAlgorithmException}, {@link javax.crypto.NoSuchPaddingException
+ * NoSuchPaddingException} and {@link java.security.NoSuchProviderException
+ * NoSuchProviderException}) as the cause.
+ *
+ * @see Cipher (Encryption) Algorithms
+ *
+ * @see StandardCipherTransformations
+ */
+ public CodecCipher(String transformation, String provider) throws IllegalArgumentException
+ {
+ this(transformation, provider, (BinaryEncoder)null, (BinaryDecoder)null);
+ }
+
+
+ /**
+ * Constructor with a transformation, a provider, an encoder and a decoder.
+ *
+ * @param transformation
+ * A transformation in the form of either "algorithm/mode/padding"
+ * or "algorithm". For example, "AES/CBC/PKCS5Padding".
+ *
+ * @param provider
+ * A provider which provides the implementation of the transformation.
+ *
+ * @param encoder
+ * An encoder used in {@link #encrypt(String)} and
+ * {@link #encrypt(byte[])} to encode an encrypted byte array.
+ * If {@code null} is given, {@link Base64} is used as the default
+ * encoder.
+ *
+ * @param decoder
+ * A decoder used in {@link #decrypt(String)} and
+ * {@link #decrypt(byte[])} to decode an encoded input byte array.
+ * If {@code null} is given, {@link Base64} is used as the default
+ * decoder.
+ *
+ * @throws IllegalArgumentException
+ * The given transformation is not supported. This exception wraps
+ * the original exception (such as {@link java.security.NoSuchAlgorithmException
+ * NoSuchAlgorithmException} and {@link javax.crypto.NoSuchPaddingException
+ * NoSuchPaddingException}) as the cause.
+ *
+ * @see Cipher (Encryption) Algorithms
+ *
+ * @see StandardCipherTransformations
+ */
+ public CodecCipher(String transformation, Provider provider, BinaryEncoder encoder, BinaryDecoder decoder) throws IllegalArgumentException
+ {
+ this(getCipherInstance(transformation, provider), encoder, decoder);
+ }
+
+
+ /**
+ * Constructor with a transformation, a provider and a coder.
+ *
+ * + * This constructor is an alias of {@link #CodecCipher(String, Provider, BinaryEncoder, BinaryDecoder) + * CodecCipher(transformation, provider, coder, coder)}. + *
+ * + * @param transformation + * A transformation in the form of either"algorithm/mode/padding"
+ * or "algorithm". For example, "AES/CBC/PKCS5Padding".
+ *
+ * @param provider
+ * A provider which provides the implementation of the transformation.
+ *
+ * @param coder
+ * A coder which works as both an encoder and a decoder.
+ * If {@code null} is given, {@link Base64} is used as the
+ * default coder.
+ *
+ * @throws IllegalArgumentException
+ * The given transformation is not supported. This exception wraps
+ * the original exception (such as {@link java.security.NoSuchAlgorithmException
+ * NoSuchAlgorithmException} and {@link javax.crypto.NoSuchPaddingException
+ * NoSuchPaddingException}) as the cause.
+ *
+ * @see Cipher (Encryption) Algorithms
+ *
+ * @see StandardCipherTransformations
+ */
+ public + * This constructor is an alias of {@link #CodecCipher(String, Provider, BinaryEncoder, BinaryDecoder) + * CodecCipher(transformation, provider, (BinaryEncoder)null, (BinaryDecoder)null)}. + *
+ * + * @param transformation + * A transformation in the form of either"algorithm/mode/padding"
+ * or "algorithm". For example, "AES/CBC/PKCS5Padding".
+ *
+ * @param provider
+ * A provider which provides the implementation of the transformation.
+ *
+ * @throws IllegalArgumentException
+ * The given transformation is not supported. This exception wraps
+ * the original exception (such as {@link java.security.NoSuchAlgorithmException
+ * NoSuchAlgorithmException} and {@link javax.crypto.NoSuchPaddingException
+ * NoSuchPaddingException}) as the cause.
+ *
+ * @see Cipher (Encryption) Algorithms
+ *
+ * @see StandardCipherTransformations
+ */
+ public CodecCipher(String transformation, Provider provider) throws IllegalArgumentException
+ {
+ this(transformation, provider, (BinaryEncoder)null, (BinaryDecoder)null);
+ }
+
+
+ /**
+ * Create a {@link Cipher} instance by {@link Cipher#getInstance(String)
+ * Cipher.getInstance(transformation)}.
+ */
+ private static Cipher getCipherInstance(String transformation) throws IllegalArgumentException
+ {
+ try
+ {
+ return Cipher.getInstance(transformation);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+
+ /**
+ * Create a {@link Cipher} instance by {@link Cipher#getInstance(String, String)
+ * Cipher.getInstance(transformation, provider)}.
+ */
+ private static Cipher getCipherInstance(String transformation, String provider)
+ {
+ try
+ {
+ return Cipher.getInstance(transformation, provider);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+
+ /**
+ * Create a {@link Cipher} instance by {@link Cipher#getInstance(String, Provider)
+ * Cipher.getInstance(transformation, provider)}.
+ */
+ private static Cipher getCipherInstance(String transformation, Provider provider)
+ {
+ try
+ {
+ return Cipher.getInstance(transformation, provider);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+
+ /**
+ * Get the cipher which this instance internally holds.
+ *
+ * @return
+ * The {@link Cipher} instance.
+ */
+ public Cipher getCipher()
+ {
+ return cipher;
+ }
+
+
+ /**
+ * Set a cipher instance.
+ *
+ * @param cipher
+ * A cipher instance.
+ *
+ * @return
+ * {@code this} object.
+ */
+ public CodecCipher setCipher(Cipher cipher)
+ {
+ if (cipher == null)
+ {
+ throw new IllegalArgumentException("cipher is null.");
+ }
+
+ this.cipher = cipher;
+
+ return this;
+ }
+
+
+ /**
+ * Get the cipher algorithm name.
+ *
+ * + * If the internal cipher is {@code null}, {@code null} is returned. + * Otherwise, the algorithm part of the cipher's transformation is + * returned. + *
+ * + * @return + * The cipher algorithm name. + */ + public String getAlgorithm() + { + if (cipher == null) + { + return null; + } + + String transformation = cipher.getAlgorithm(); + + if (transformation == null) + { + return null; + } + + // Separator position. + int pos = transformation.indexOf('/'); + + if (pos < 0) + { + return transformation; + } + + return transformation.substring(0, pos); + } + + + /** + * Get the encoder which this instance internally holds. + * + * @return + * The internal encoder. This may be {@code null}, and + * in such a case, {@link #encrypt(String)} and + * {@link #encrypt(byte[])} use {@link Base64}. + */ + public BinaryEncoder getEncoder() + { + return encoder; + } + + + /** + * Set an encoder. + * + * @param encoder + * An encoder used by {@link #encrypt(String)} and + * {@link #encrypt(byte[])}. + * + * @return + * {@code this} object. + */ + public CodecCipher setEncoder(BinaryEncoder encoder) + { + this.encoder = encoder; + + return this; + } + + + /** + * Get the decoder which this instance internally holds. + * + * @return + * The internal decoder. This may be {@code null}, and + * in such a case, {@link #decrypt(String)} and + * {@link #decrypt(byte[])} use {@link Base64}. + */ + public BinaryDecoder getDecoder() + { + return decoder; + } + + + /** + * Set a decoder. + * + * @param decoder + * A decoder used by {@link #decrypt(String)} and + * {@link #decrypt(byte[])}. + * + * @return + * {@code this} object. + */ + public CodecCipher setDecoder(BinaryDecoder decoder) + { + this.decoder = decoder; + + return this; + } + + + /** + * Set a coder. + * + * @param coder + * A coder which works as both an encoder and a decoder. + * If {@code null} is given, {@link Base64} is used as the + * default coder. + * + * @return + * {@code this} object. + */ + public+ * If this method is used to set initialization parameters, + * {@link Cipher#init(int, Key) Cipher.init(mode, (Key)key)} + * is called later from within {@code encrypt}/{@code decrypt} methods. + *
+ * + * @param key + * + * @return + * {@code this} object. + * + * @throws IllegalArgumentException + * {@code key} is {@code null}. + */ + public CodecCipher setInit(Key key) throws IllegalArgumentException + { + return setInit(key, null, null, null); + } + + + /** + * Set cipher initialization parameters. + * + *+ * If this method is used to set initialization parameters, + * {@link Cipher#init(int, Key, SecureRandom) + * Cipher.init(mode, (Key)key, (SecureRandom)random)} + * is called later from within {@code encrypt}/{@code decrypt} methods. + *
+ * + * @param key + * + * @param random + * + * @return + * {@code this} object. + * + * @throws IllegalArgumentException + * {@code key} is {@code null}. + */ + public CodecCipher setInit(Key key, SecureRandom random) throws IllegalArgumentException + { + return setInit(key, null, null, random); + } + + + /** + * Set cipher initialization parameters. + * + *+ * If this method is used to set initialization parameters, + * {@link Cipher#init(int, Key, AlgorithmParameters) + * Cipher.init(mode, (Key)key, (AlgorithmParameters)params)} + * is called later from within {@code encrypt}/{@code decrypt} methods. + *
+ * + * @param key + * + * @param params + * + * @return + * {@code this} object. + * + * @throws IllegalArgumentException + * {@code key} is {@code null}. + */ + public CodecCipher setInit(Key key, AlgorithmParameters params) throws IllegalArgumentException + { + return setInit(key, params, null, null); + } + + + /** + * Set cipher initialization parameters. + * + *+ * If this method is used to set initialization parameters, + * {@link Cipher#init(int, Key, AlgorithmParameters, SecureRandom) + * Cipher.init(mode, (Key)key, (AlgorithmParameters)params, (SecureRandom)random)} + * is called later from within {@code encrypt}/{@code decrypt} methods. + *
+ * + * @param key + * + * @param params + * + * @param random + * + * @return + * {@code this} object. + * + * @throws IllegalArgumentException + * {@code key} is {@code null}. + */ + public CodecCipher setInit(Key key, AlgorithmParameters params, SecureRandom random) throws IllegalArgumentException + { + return setInit(key, params, null, random); + } + + + /** + * Set cipher initialization parameters. + * + *+ * If this method is used to set initialization parameters, + * {@link Cipher#init(int, Key, AlgorithmParameterSpec) + * Cipher.init(mode, (Key)key, (AlgorithmParameterSpec)spec)} + * is called later from within {@code encrypt}/{@code decrypt} methods. + *
+ * + * @param key + * + * @param spec + * + * @return + * {@code this} object. + * + * @throws IllegalArgumentException + * {@code key} is {@code null}. + */ + public CodecCipher setInit(Key key, AlgorithmParameterSpec spec) throws IllegalArgumentException + { + return setInit(key, null, spec, null); + } + + + /** + * Set cipher initialization parameters. + * + *+ * If this method is used to set initialization parameters, + * {@link Cipher#init(int, Key, AlgorithmParameterSpec, SecureRandom) + * Cipher.init(mode, (Key)key, (AlgorithmParameterSpec)spec, (SecureRandom)random)} + * is called later from within {@code encrypt}/{@code decrypt} methods. + *
+ * + * @param key + * + * @param spec + * + * @param random + * + * @return + * {@code this} object. + * + * @throws IllegalArgumentException + * {@code key} is {@code null}. + */ + public CodecCipher setInit(Key key, AlgorithmParameterSpec spec, SecureRandom random) throws IllegalArgumentException + { + return setInit(key, null, spec, random); + } + + + /** + * Set cipher initialization parameters. + * + *+ * If this method is used to set initialization parameters, + * {@link Cipher#init(int, Certificate) + * Cipher.init(mode, (Certificate)certificate)} + * is called later from within {@code encrypt}/{@code decrypt} methods. + *
+ * + * @param certificate + * + * @return + * {@code this} object. + * + * @throws IllegalArgumentException + * {@code certificate} is {@code null}. + */ + public CodecCipher setInit(Certificate certificate) throws IllegalArgumentException + { + return setInit(certificate, null); + } + + + /** + * Set cipher initialization parameters. + * + *+ * If this method is used to set initialization parameters, + * {@link Cipher#init(int, Certificate, SecureRandom) + * Cipher.init(mode, (Certificate)certificate, (SecureRandom)random)} + * is called later from within {@code encrypt}/{@code decrypt} methods. + *
+ * + * @param certificate + * + * @param random + * + * @return + * {@code this} object. + * + * @throws IllegalArgumentException + * {@code certificate} is {@code null}. + */ + public CodecCipher setInit(Certificate certificate, SecureRandom random) throws IllegalArgumentException + { + if (certificate == null) + { + throw new IllegalArgumentException("certificate is null."); + } + + return setInit(null, null, null, certificate, random); + } + + + /** + * Set cipher initialization parameters. + */ + private CodecCipher setInit(Key key, AlgorithmParameters params, AlgorithmParameterSpec spec, SecureRandom random) + { + if (key == null) + { + throw new IllegalArgumentException("key is null."); + } + + return setInit(key, params, spec, null, random); + } + + + /** + * Set cipher initialization parameters. + */ + private CodecCipher setInit(Key key, AlgorithmParameters params, AlgorithmParameterSpec spec, Certificate certificate, SecureRandom random) + { + this.key = key; + this.params = params; + this.spec = spec; + this.certificate = certificate; + this.random = random; + + return this; + } + + + /** + * Encrypt the given string. + * + * @param input + * Plain text before encryption. + * + * @return + * Encrypted string encoded by the encoder. + * If {@code input} is {@code null}, {@code null} is returned. + * + * @throws IllegalStateException + * Failed to encrypt the input. The {@code IllegalStateException} + * wraps the original exception (such as {@link InvalidKeyException} + * and {@link EncoderException}) as the cause, if any. + *+ * The list here is a copy from Java SE 7 JavaDoc. Therefore, + * they may not be supported in older Java SE environments. + *
+ * + * @see javax.crypto.Cipher + * + * @author Takahiko Kawasaki + * @since 4.23 + */ +public class StandardCipherTransformations +{ + /** + * AES/CBC/NoPadding (128) + */ + public static final String AES_CBC_NOPADDING = "AES/CBC/NoPadding"; + + + /** + * AES/CBC/PKCS5Padding (128) + */ + public static final String AES_CBC_PKCS5PADDING = "AES/CBC/PKCS5Padding"; + + + /** + * AES/ECB/NoPadding (128) + */ + public static final String AES_ECB_NOPADDING = "AES/ECB/NoPadding"; + + + /** + * AES/ECB/PKCS5Padding (128) + */ + public static final String AES_ECB_PKCS5PADDING = "AES/ECB/PKCS5Padding"; + + + /** + * DES/CBC/NoPadding (56) + */ + public static final String DES_CBC_NOPADDING = "DES/CBC/NoPadding"; + + + /** + * DES/CBC/PKCS5Padding (56) + */ + public static final String DES_CBC_PKCS5PADDING = "DES/CBC/PKCS5Padding"; + + + /** + * DES/ECB/NoPadding (56) + */ + public static final String DES_ECB_NOPADDING = "DES/ECB/NoPadding"; + + + /** + * DES/ECB/PKCS5Padding (56) + */ + public static final String DES_ECB_PKCS5PADDING = "DES/ECB/PKCS5Padding"; + + + /** + * DESede/CBC/NoPadding (168) + */ + public static final String DESEDE_CBC_NOPADDING = "DESede/CBC/NoPadding"; + + + /** + * DESede/CBC/PKCS5Padding (168) + */ + public static final String DESEDE_CBC_PKCS5PADDING = "DESede/CBC/PKCS5Padding"; + + + /** + * DESede/ECB/NoPadding (168) + */ + public static final String DESEDE_ECB_NOPADDING = "DESede/ECB/NoPadding"; + + + /** + * DESede/ECB/PKCS5Padding (168) + */ + public static final String DESEDE_ECB_PKCS5PADDING = "DESede/ECB/PKCS5Padding"; + + + /** + * RSA/ECB/PKCS1Padding (1024, 2048) + */ + public static final String RSA_ECB_PKCS1PADDING = "RSA/ECB/PKCS1Padding"; + + + /** + * RSA/ECB/OAEPWithSHA-1AndMGF1Padding (1024, 2048) + */ + public static final String RSA_ECB_OAEPWITHSHA1ANDMGF1PADDING = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding"; + + + /** + * RSA/ECB/OAEPWithSHA-256AndMGF1Padding (1024, 2048) + */ + public static final String RSA_ECB_OAEPWITHSHA256ANDMGF1PADDING = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"; + + + private StandardCipherTransformations() + { + } +} diff --git a/src/main/java/com/authlete/common/util/security/Utils.java b/src/main/java/com/authlete/common/util/security/Utils.java new file mode 100644 index 0000000000..9977d45a92 --- /dev/null +++ b/src/main/java/com/authlete/common/util/security/Utils.java @@ -0,0 +1,113 @@ +package com.authlete.common.util.security; + + +import java.io.UnsupportedEncodingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + + +/** + * Utility methods for internal implementation. + * + * @author Takahiko Kawasaki + * @since 4.23 + */ +class Utils +{ + private Utils() + { + } + + + /** + * Get bytes by {@code input.getBytes("UTF-8")}. + */ + public static byte[] getBytesUTF8(String string) + { + if (string == null) + { + return null; + } + + try + { + // Convert the string to a byte array encoded in UTF-8. + return string.getBytes("UTF-8"); + } + catch (UnsupportedEncodingException e) + { + // This won't happen. + return null; + } + } + + + /** + * Build a {@code String} instance by {@code new String(input, "UTF-8")}. + */ + public static String toStringUTF8(byte[] input) + { + if (input == null) + { + return null; + } + + try + { + return new String(input, "UTF-8"); + } + catch (UnsupportedEncodingException e) + { + // This won't happen. + return null; + } + } + + + public static byte[] ensureSize(byte[] data, int size) + { + if (data == null) + { + return new byte[size]; + } + + if (size <= data.length) + { + return data; + } + + byte[] data2 = new byte[size]; + + System.arraycopy(data, 0, data2, 0, data.length); + + return data2; + } + + + public static SecretKeySpec createSecretKeySpec(String key, String algorithm, int size) + { + return createSecretKeySpec(getBytesUTF8(key), algorithm, size); + } + + + public static SecretKeySpec createSecretKeySpec(byte[] key, String algorithm, int size) + { + key = ensureSize(key, size); + + return new SecretKeySpec(key, 0, size, algorithm); + } + + + public static IvParameterSpec createIvParameterSpec(String iv, int size) + { + return createIvParameterSpec(getBytesUTF8(iv), size); + } + + + public static IvParameterSpec createIvParameterSpec(byte[] iv, int size) + { + iv = ensureSize(iv, size); + + return new IvParameterSpec(iv, 0, size); + } +} diff --git a/src/test/java/com/authlete/common/api/AccessRightTest.java b/src/test/java/com/authlete/common/api/AccessRightTest.java new file mode 100644 index 0000000000..5ef8a6161a --- /dev/null +++ b/src/test/java/com/authlete/common/api/AccessRightTest.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2026 Authlete, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.authlete.common.api; + + +import static com.authlete.common.api.AccessRight.*; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.Test; + + +public class AccessRightTest +{ + private static final long SERVICE_ID = 1L; + private static final long CLIENT_ID = 2L; + private static final long NONE = 0L; + + + // --- canDo: identity --- + + @Test + public void testCanDoSelf() + { + assertTrue(USE_INTROSPECTION.canDo(NONE, SERVICE_ID, USE_INTROSPECTION)); + assertTrue(USE_SERVICE.canDo(NONE, SERVICE_ID, USE_SERVICE)); + assertTrue(MODIFY_SERVICE.canDo(NONE, SERVICE_ID, MODIFY_SERVICE)); + assertTrue(CREATE_CLIENT.canDo(NONE, SERVICE_ID, CREATE_CLIENT)); + assertTrue(ADMIN.canDo(NONE, NONE, ADMIN)); + } + + + // --- canDo: USE_INTROSPECTION subsumption --- + + @Test + public void testUseServiceIncludesUseIntrospection() + { + assertTrue(USE_SERVICE.canDo(NONE, SERVICE_ID, USE_INTROSPECTION)); + } + + @Test + public void testModifyServiceIncludesUseIntrospection() + { + assertTrue(MODIFY_SERVICE.canDo(NONE, SERVICE_ID, USE_INTROSPECTION)); + } + + @Test + public void testCreateClientIncludesUseIntrospection() + { + assertTrue(CREATE_CLIENT.canDo(NONE, SERVICE_ID, USE_INTROSPECTION)); + } + + @Test + public void testAdminIncludesUseIntrospection() + { + assertTrue(ADMIN.canDo(NONE, SERVICE_ID, USE_INTROSPECTION)); + } + + + // --- canDo: VIEW_SERVICE subsumption --- + + @Test + public void testUseServiceIncludesViewService() + { + assertTrue(USE_SERVICE.canDo(NONE, SERVICE_ID, VIEW_SERVICE)); + } + + @Test + public void testModifyServiceIncludesViewService() + { + assertTrue(MODIFY_SERVICE.canDo(NONE, SERVICE_ID, VIEW_SERVICE)); + } + + @Test + public void testCreateClientIncludesViewService() + { + assertTrue(CREATE_CLIENT.canDo(NONE, SERVICE_ID, VIEW_SERVICE)); + } + + + // --- canDo: missing service/client IDs --- + + @Test + public void testServiceSpecificRequiresServiceId() + { + assertFalse(USE_INTROSPECTION.canDo(NONE, NONE, USE_INTROSPECTION)); + assertFalse(USE_SERVICE.canDo(NONE, NONE, USE_SERVICE)); + assertFalse(MODIFY_SERVICE.canDo(NONE, NONE, MODIFY_SERVICE)); + } + + @Test + public void testClientSpecificRequiresBoth() + { + assertFalse(VIEW_CLIENT.canDo(NONE, SERVICE_ID, VIEW_CLIENT)); + assertFalse(VIEW_CLIENT.canDo(CLIENT_ID, NONE, VIEW_CLIENT)); + assertTrue(VIEW_CLIENT.canDo(CLIENT_ID, SERVICE_ID, VIEW_CLIENT)); + } + + + // --- canDo: no upward escalation --- + + @Test + public void testUseIntrospectionDoesNotIncludeUseService() + { + assertFalse(USE_INTROSPECTION.canDo(NONE, SERVICE_ID, USE_SERVICE)); + } + + @Test + public void testViewServiceDoesNotIncludeModifyService() + { + assertFalse(VIEW_SERVICE.canDo(NONE, SERVICE_ID, MODIFY_SERVICE)); + } + + @Test + public void testUseServiceDoesNotIncludeModifyService() + { + assertFalse(USE_SERVICE.canDo(NONE, SERVICE_ID, MODIFY_SERVICE)); + } + + + // --- canDoAny: USE_INTROSPECTION --- + + @Test + public void testCanDoAnyUseServiceIncludesUseIntrospection() + { + assertTrue(USE_SERVICE.canDoAny(SERVICE_ID, USE_INTROSPECTION)); + } + + @Test + public void testCanDoAnyModifyServiceIncludesUseIntrospection() + { + assertTrue(MODIFY_SERVICE.canDoAny(SERVICE_ID, USE_INTROSPECTION)); + } + + @Test + public void testCanDoAnyCreateClientIncludesUseIntrospection() + { + assertTrue(CREATE_CLIENT.canDoAny(SERVICE_ID, USE_INTROSPECTION)); + } + + @Test + public void testCanDoAnyAdminIncludesUseIntrospection() + { + assertTrue(ADMIN.canDoAny(SERVICE_ID, USE_INTROSPECTION)); + } + + @Test + public void testCanDoAnyClientSpecificRequiresServiceId() + { + // VIEW_CLIENT is clientSpecific — canDoAny requires a serviceId in that case + assertFalse(VIEW_CLIENT.canDoAny(NONE, VIEW_CLIENT)); + assertTrue(VIEW_CLIENT.canDoAny(SERVICE_ID, VIEW_CLIENT)); + } +} diff --git a/src/test/java/com/authlete/common/assurance/VerifiedClaimsTest.java b/src/test/java/com/authlete/common/assurance/VerifiedClaimsTest.java index 6cdd7dedbb..1d5defac96 100644 --- a/src/test/java/com/authlete/common/assurance/VerifiedClaimsTest.java +++ b/src/test/java/com/authlete/common/assurance/VerifiedClaimsTest.java @@ -21,9 +21,9 @@ import java.io.IOException; import java.util.LinkedHashMap; import java.util.Map; +import com.authlete.common.util.digest.Digest; import org.junit.Test; import com.google.gson.Gson; -import com.neovisionaries.security.Digest; public class VerifiedClaimsTest diff --git a/src/test/java/com/authlete/common/assurance/constraint/VerifiedClaimsContainerConstraintTest.java b/src/test/java/com/authlete/common/assurance/constraint/VerifiedClaimsContainerConstraintTest.java index a4f1263f7c..bd271178e6 100644 --- a/src/test/java/com/authlete/common/assurance/constraint/VerifiedClaimsContainerConstraintTest.java +++ b/src/test/java/com/authlete/common/assurance/constraint/VerifiedClaimsContainerConstraintTest.java @@ -19,19 +19,8 @@ import static org.junit.Assert.*; import java.io.IOException; +import com.authlete.common.util.digest.Digest; import org.junit.Test; -import com.authlete.common.assurance.constraint.ClaimsConstraint; -import com.authlete.common.assurance.constraint.EvidenceArrayConstraint; -import com.authlete.common.assurance.constraint.EvidenceConstraint; -import com.authlete.common.assurance.constraint.IDDocumentConstraint; -import com.authlete.common.assurance.constraint.LeafConstraint; -import com.authlete.common.assurance.constraint.QESConstraint; -import com.authlete.common.assurance.constraint.TimeConstraint; -import com.authlete.common.assurance.constraint.UtilityBillConstraint; -import com.authlete.common.assurance.constraint.VerificationConstraint; -import com.authlete.common.assurance.constraint.VerifiedClaimsConstraint; -import com.authlete.common.assurance.constraint.VerifiedClaimsContainerConstraint; -import com.neovisionaries.security.Digest; public class VerifiedClaimsContainerConstraintTest diff --git a/src/test/java/com/authlete/common/dto/AuthzDetailsElementTest.java b/src/test/java/com/authlete/common/dto/AuthzDetailsElementTest.java index 3210ed2af9..0db0d215a9 100644 --- a/src/test/java/com/authlete/common/dto/AuthzDetailsElementTest.java +++ b/src/test/java/com/authlete/common/dto/AuthzDetailsElementTest.java @@ -16,10 +16,10 @@ package com.authlete.common.dto; +import com.authlete.common.util.digest.Digest; import org.junit.Test; import com.google.gson.Gson; import com.google.gson.JsonParseException; -import com.neovisionaries.security.Digest; import static org.junit.Assert.*; import java.io.IOException; import java.util.ArrayList; diff --git a/src/test/java/com/authlete/common/dto/CredentialIssuerMetadataTest.java b/src/test/java/com/authlete/common/dto/CredentialIssuerMetadataTest.java index 39b755295a..fc5eaf1f20 100644 --- a/src/test/java/com/authlete/common/dto/CredentialIssuerMetadataTest.java +++ b/src/test/java/com/authlete/common/dto/CredentialIssuerMetadataTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024 Authlete, Inc. + * Copyright (C) 2024-2025 Authlete, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,8 +19,11 @@ import org.junit.Test; import com.authlete.common.types.JWEAlg; import com.authlete.common.types.JWEEnc; +import com.authlete.common.types.JWEZip; +import com.nimbusds.jose.jwk.JWK; +import com.nimbusds.jose.jwk.JWKSet; import static org.junit.Assert.*; -import java.net.URI; +import java.text.ParseException; import java.util.List; import java.util.Map; @@ -29,31 +32,87 @@ public class CredentialIssuerMetadataTest { @SuppressWarnings("unchecked") @Test - public void test_toMap() + public void test_toMap_request_encryption() throws ParseException { - URI issuer = URI.create("https://example.com"); - JWEAlg[] algs = new JWEAlg[] { JWEAlg.ECDH_ES }; - JWEEnc[] encs = new JWEEnc[] { JWEEnc.A128CBC_HS256 }; + String jwks = + "{\n" + + " \"keys\": [\n" + + " {\n" + + " \"kty\": \"EC\",\n" + + " \"crv\": \"P-256\",\n" + + " \"x\": \"8fpkqvB9-NjASgqR4KnM9s1gjyaDZpYYp9E65c7glns\",\n" + + " \"y\": \"SdbSFS314C2N415VQ7IaXkxHRH34aA32csKOs_JipSg\",\n" + + " \"d\": \"gTVIUWvmYAW2WT_lOMiQGBK2BbRHOhew-sO5JBTCkp4\",\n" + + " \"kid\": \"TxNdpxeiLUxuPXh_INadmBVkOqk77pmYLL7BthTpZsA\"\n" + + " }\n" + + " ]\n" + + "}\n" + + ""; + JWEEnc[] encs = new JWEEnc[] { JWEEnc.A128CBC_HS256 }; + JWEZip[] zips = new JWEZip[] { JWEZip.DEF }; + + CredentialIssuerMetadata metadata = new CredentialIssuerMetadata() + .setCredentialRequestEncryptionJwks(jwks) + .setCredentialRequestEncryptionEncValuesSupported(encs) + .setCredentialRequestEncryptionZipValuesSupported(zips) + .setRequireCredentialRequestEncryption(true) + ; + + Map