From 56bf4d12dfd877919e3ab1c23a798f37fc1638ee Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 18 Jul 2025 12:04:53 +0000
Subject: [PATCH 001/132] Bump com.nimbusds:nimbus-jose-jwt from 9.31 to 10.0.2
Bumps [com.nimbusds:nimbus-jose-jwt](https://bitbucket.org/connect2id/nimbus-jose-jwt) from 9.31 to 10.0.2.
- [Changelog](https://bitbucket.org/connect2id/nimbus-jose-jwt/src/master/CHANGELOG.txt)
- [Commits](https://bitbucket.org/connect2id/nimbus-jose-jwt/branches/compare/10.0.2..9.31)
---
updated-dependencies:
- dependency-name: com.nimbusds:nimbus-jose-jwt
dependency-version: 10.0.2
dependency-type: direct:production
...
Signed-off-by: dependabot[bot]
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index b35dc48f6..80be4e8ed 100644
--- a/pom.xml
+++ b/pom.xml
@@ -32,7 +32,7 @@
UTF-8
scm:git:git@github.com:authlete/authlete-java-common.git
git@github.com:authlete/authlete-java-common.git
- 9.31
+ 10.0.2
From c0a651c241a33d209749b0fbb2eb4fac9429c399 Mon Sep 17 00:00:00 2001
From: abhinavbhagat <50474764+abhinavbhagat@users.noreply.github.com>
Date: Wed, 23 Jul 2025 20:45:56 +0900
Subject: [PATCH 002/132] fix distribution management (#132)
Co-authored-by: abhinav.bhagat
---
pom.xml | 11 -----------
1 file changed, 11 deletions(-)
diff --git a/pom.xml b/pom.xml
index f23eb457c..3e82417c1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -181,17 +181,6 @@
external
-
-
- authlete
- https://s01.oss.sonatype.org/content/repositories/snapshots/
-
-
-
- authlete
- https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/
-
-
authlete-internal
From 00fb41ec61b94a02d1f4f53c3234c748b58b1cd6 Mon Sep 17 00:00:00 2001
From: AuthleteOPS <149344570+AuthleteOPS@users.noreply.github.com>
Date: Wed, 23 Jul 2025 07:59:22 -0400
Subject: [PATCH 003/132] fix snapshot (#133)
* fix snapshot
* fix snapshot
---------
Co-authored-by: abhinav.bhagat
---
pom.xml | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 3e82417c1..6fd254572 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.21-SNAPSHOT
+ 4.21
jar
${project.groupId}:${project.artifactId}
@@ -181,6 +181,17 @@
external
+
+
+ authlete
+ https://s01.oss.sonatype.org/content/repositories/snapshots/
+
+
+
+ authlete
+ https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/
+
+
authlete-internal
From 4db5a60aa00be8821375694123c253415e96c9ae Mon Sep 17 00:00:00 2001
From: AuthleteOPS <149344570+AuthleteOPS@users.noreply.github.com>
Date: Wed, 23 Jul 2025 21:09:33 -0400
Subject: [PATCH 004/132] fix ver (#134)
Co-authored-by: abhinav.bhagat
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 6fd254572..f23eb457c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.21
+ 4.21-SNAPSHOT
jar
${project.groupId}:${project.artifactId}
From e959cdef260c6fc5d910f8b94993a76d6ec1eec0 Mon Sep 17 00:00:00 2001
From: Authlete admin
Date: Thu, 24 Jul 2025 01:11:20 +0000
Subject: [PATCH 005/132] [maven-release-plugin] prepare release
authlete-java-common-4.21
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index f23eb457c..6fd254572 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.21-SNAPSHOT
+ 4.21
jar
${project.groupId}:${project.artifactId}
From 2053b4a307bcba9e1034e42f4e818de38e2ac3d2 Mon Sep 17 00:00:00 2001
From: Authlete admin
Date: Thu, 24 Jul 2025 01:11:22 +0000
Subject: [PATCH 006/132] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 6fd254572..93e3d4afe 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.21
+ 4.22-SNAPSHOT
jar
${project.groupId}:${project.artifactId}
From 9c627718488879e59ebeef41f934ae95f6e829e0 Mon Sep 17 00:00:00 2001
From: abhinavbhagat <50474764+abhinavbhagat@users.noreply.github.com>
Date: Thu, 24 Jul 2025 17:29:29 +0900
Subject: [PATCH 007/132] adding a tag for autopublish (#135)
Co-authored-by: abhinav.bhagat
---
pom.xml | 1 +
1 file changed, 1 insertion(+)
diff --git a/pom.xml b/pom.xml
index 93e3d4afe..1f0037c0f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -213,6 +213,7 @@
true
authlete
+ true
From f65b16c436c76f439bd55a47a5bbb83363ba1230 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Thu, 14 Aug 2025 06:06:05 +0000
Subject: [PATCH 008/132] [maven-release-plugin] prepare release
authlete-java-common-4.22
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 24218270b..0cc0825d9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.22-SNAPSHOT
+ 4.22
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.21
+ authlete-java-common-4.22
From 66a0efae2988fbd0e858bc53811bab22b9fc7f19 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Thu, 14 Aug 2025 06:06:07 +0000
Subject: [PATCH 009/132] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 0cc0825d9..0c6aff1be 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.22
+ 4.23-SNAPSHOT
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.22
+ authlete-java-common-4.21
From 2c77a1b5e0142e18a944162af26b760f4948f506 Mon Sep 17 00:00:00 2001
From: Hideki Ikeda
Date: Thu, 14 Aug 2025 16:50:27 +0900
Subject: [PATCH 010/132] Updated documents for version 4.22.
---
CHANGES.ja.md | 6 ++++++
CHANGES.md | 6 ++++++
src/main/java/com/authlete/common/package-info.java | 4 ++--
3 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index 5c1fa8960..be801636e 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,12 @@
変更点
======
+4.22 (2025 年 08 月 14 日)
+-----------------
+
+- nimbus-jose-jwt を バージョン 10.0.2 に更新。
+
+
4.21 (2025 年 07 月 18 日)
-----------------
diff --git a/CHANGES.md b/CHANGES.md
index 134f0b1e8..b60a19191 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,12 @@
CHANGES
=======
+4.22 (2025-08-14)
+-----------------
+
+- Updated nimbus-jose-jwt to v10.0.2.
+
+
4.21 (2025-07-18)
-----------------
diff --git a/src/main/java/com/authlete/common/package-info.java b/src/main/java/com/authlete/common/package-info.java
index b50466ba7..feadfb82e 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.22</version>
* </dependency>
*
- * @version 4.21
+ * @version 4.22
*/
package com.authlete.common;
From 1c83f9b75a5dbb9e15ccb9647e1c7b80d733bc2f Mon Sep 17 00:00:00 2001
From: Antoine Resenterra <45132539+Toinetoine1@users.noreply.github.com>
Date: Thu, 21 Aug 2025 14:41:55 +0900
Subject: [PATCH 011/132] Remove dependency on nv-cipher and nv-digest (#137)
feat: remove nv-cipher and nv-digest
Co-authored-by: Antoine Resenterra
---
pom.xml | 33 +-
.../conf/AuthletePropertiesConfiguration.java | 2 +-
.../authlete/common/util/digest/Digest.java | 2444 +++++++++++++++++
.../common/util/digest/JsonDigestUpdater.java | 398 +++
.../common/util/security/AESCipher.java | 554 ++++
.../common/util/security/CodecCipher.java | 1209 ++++++++
.../StandardCipherTransformations.java | 114 +
.../authlete/common/util/security/Utils.java | 113 +
.../common/assurance/VerifiedClaimsTest.java | 2 +-
...VerifiedClaimsContainerConstraintTest.java | 13 +-
.../common/dto/AuthzDetailsElementTest.java | 2 +-
.../common/ida/DatasetExtractorTest.java | 2 +-
12 files changed, 4856 insertions(+), 30 deletions(-)
create mode 100644 src/main/java/com/authlete/common/util/digest/Digest.java
create mode 100644 src/main/java/com/authlete/common/util/digest/JsonDigestUpdater.java
create mode 100644 src/main/java/com/authlete/common/util/security/AESCipher.java
create mode 100644 src/main/java/com/authlete/common/util/security/CodecCipher.java
create mode 100644 src/main/java/com/authlete/common/util/security/StandardCipherTransformations.java
create mode 100644 src/main/java/com/authlete/common/util/security/Utils.java
diff --git a/pom.xml b/pom.xml
index 0c6aff1be..b21d15c70 100644
--- a/pom.xml
+++ b/pom.xml
@@ -43,12 +43,6 @@
-
- com.neovisionaries
- nv-cipher
- 1.3
-
-
com.google.code.gson
gson
@@ -68,23 +62,34 @@
test
+
+ com.fasterxml.jackson.core
+ jackson-core
+ 2.19.2
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.19.2
+
+
com.fasterxml.jackson.core
jackson-annotations
- 2.18.2
+ 2.19.2
- junit
- junit
- 4.13.1
- test
+ commons-codec
+ commons-codec
+ 1.19.0
- com.neovisionaries
- nv-digest
- 1.5
+ junit
+ junit
+ 4.13.1
test
diff --git a/src/main/java/com/authlete/common/conf/AuthletePropertiesConfiguration.java b/src/main/java/com/authlete/common/conf/AuthletePropertiesConfiguration.java
index e5f5cc1d9..2ecc26b2e 100644
--- a/src/main/java/com/authlete/common/conf/AuthletePropertiesConfiguration.java
+++ b/src/main/java/com/authlete/common/conf/AuthletePropertiesConfiguration.java
@@ -19,7 +19,7 @@
import java.util.logging.Logger;
import com.authlete.common.util.PropertiesLoader;
import com.authlete.common.util.TypedProperties;
-import com.neovisionaries.security.AESCipher;
+import com.authlete.common.util.security.AESCipher;
/**
diff --git a/src/main/java/com/authlete/common/util/digest/Digest.java b/src/main/java/com/authlete/common/util/digest/Digest.java
new file mode 100644
index 000000000..e9c2de26d
--- /dev/null
+++ b/src/main/java/com/authlete/common/util/digest/Digest.java
@@ -0,0 +1,2444 @@
+package com.authlete.common.util.digest;
+
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.security.DigestException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.util.HashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import org.apache.commons.codec.BinaryEncoder;
+import org.apache.commons.codec.EncoderException;
+
+
+/**
+ * A wrapper class over MessageDigest with many {@code update}
+ * methods in a fluent style, meaning {@code update} methods
+ * can be chained.
+ *
+ *
+ * {@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 mFeatures;
+
+
+ /**
+ * Constructor with a {@link MessageDigest} instance.
+ *
+ * @param messageDigest
+ * A {@link MessageDigest} instance wrapped in this
+ * {@code Digest} instance.
+ *
+ * @throws IllegalArgumentException
+ * The given argument is null.
+ */
+ public Digest(MessageDigest messageDigest)
+ {
+ if (messageDigest == null)
+ {
+ throw new IllegalArgumentException("messageDigest is null");
+ }
+
+ mMessageDigest = messageDigest;
+ mFeatures = createFeatureMap();
+ }
+
+
+ /**
+ * Create a feature map with default values.
+ */
+ private HashMap createFeatureMap()
+ {
+ HashMap map = new HashMap();
+
+ map.put(Feature.IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_NULL, Boolean.FALSE);
+ map.put(Feature.IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_FALSE, Boolean.FALSE);
+ map.put(Feature.IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_ZERO, Boolean.FALSE);
+ map.put(Feature.IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_EMPTY_STRING, Boolean.FALSE);
+ map.put(Feature.IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_EMPTY_ARRAY, Boolean.FALSE);
+ map.put(Feature.IGNORE_JSON_OBJECT_ENTRY_WITH_VALUE_EMPTY_OBJECT, Boolean.FALSE);
+ map.put(Feature.SORT_JSON_OBJECT_ENTRY_KEYS, Boolean.TRUE);
+
+ return map;
+ }
+
+
+ /**
+ * Constructor with an algorithm name.
+ *
+ *
+ * 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)mFeatures.clone();
+
+ return cloned;
+ }
+
+
+ /**
+ * Complete the hash computation. The digest is reset after
+ * this call is made.
+ *
+ *
+ * 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
+ *
+ * {@code offset < 0}
+ * {@code size <= offset}
+ * {@code length < 0}
+ * {@code size < (offset + length)}
+ *
+ */
+ private void checkRange(int size, int offset, int length)
+ {
+ String message = null;
+
+ if (offset < 0)
+ {
+ message = "The offset is less than 0.";
+ }
+ else if (size <= offset)
+ {
+ message = "The offset is equal to or greater than the size.";
+ }
+ else if (length < 0)
+ {
+ message = "The length is less than 0.";
+ }
+ else if (size < (offset + length))
+ {
+ message = "The sum of the offset and the length is greater than the size.";
+ }
+
+ if (message != null)
+ {
+ throw new IllegalArgumentException(message);
+ }
+ }
+
+
+ /**
+ * Update the wrapped {@code MessageDigest} object with the
+ * given input data.
+ *
+ *
+ * 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.
+ *
+ *
+ *
+ * @param number
+ * Input data. If null or none of the above, update is not performed.
+ *
+ * @return
+ * {@code this} object.
+ */
+ public Digest update(Number number)
+ {
+ if (number == null)
+ {
+ return this;
+ }
+
+ // Byte
+ if (number instanceof Byte)
+ {
+ update(((Byte)number).byteValue());
+ }
+ // Short
+ else if (number instanceof Short)
+ {
+ update(((Short)number).shortValue());
+ }
+ // Integer
+ else if (number instanceof Integer)
+ {
+ update(((Integer)number).intValue());
+ }
+ // Long
+ else if (number instanceof Long)
+ {
+ update(((Long)number).longValue());
+ }
+ // Float
+ else if (number instanceof Float)
+ {
+ update(((Float)number).floatValue());
+ }
+ // Double
+ else if (number instanceof Double)
+ {
+ update(((Double)number).doubleValue());
+ }
+ // BigInteger
+ else if (number instanceof BigInteger)
+ {
+ update(((BigInteger)number).toByteArray());
+ }
+ // BigDecimal
+ else if (number instanceof BigDecimal)
+ {
+ update(((BigDecimal)number).toString());
+ }
+ // AtomicInteger
+ else if (number instanceof AtomicInteger)
+ {
+ update(((AtomicInteger)number).intValue());
+ }
+ // AtomicLong
+ else if (number instanceof AtomicLong)
+ {
+ update(((AtomicLong)number).longValue());
+ }
+
+ return this;
+ }
+
+
+ /**
+ * Update the wrapped {@code MessageDigest} object with the
+ * given input data.
+ *
+ *
+ * This method is an alias of {@link #update(Number[], int, int)
+ * update}{@code (input, 0, input.length)}.
+ *
+ *
+ * @param
+ * The subclass of the {@link Number} class.
+ *
+ * @param input
+ * Input data. If null is given, update is not performed.
+ *
+ * @return
+ * {@code this} object.
+ */
+ public Digest update(TNumber[] 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(Number)} for each
+ * array element which is in the specified range.
+ *
+ *
+ * @param
+ * The subclass of the {@link Number} class.
+ *
+ * @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(TNumber[] 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 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 000000000..5026a2e54
--- /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 names = getFieldNames(node);
+
+ // For each field name.
+ for (String name : names)
+ {
+ // Get the value of the field.
+ JsonNode value = node.findValue(name);
+
+ // Update the digest with the pair of name and value.
+ updateObjectEntry(name, value);
+ }
+
+ // Object end.
+ mark("o");
+ }
+
+
+ private void updateObjectEntry(String name, JsonNode value)
+ {
+ if (shouldIgnore(value))
+ {
+ // Ignore this entry.
+ return;
+ }
+
+ // Key start.
+ mark("K");
+
+ // Update the digest with the name.
+ digest.update(name);
+
+ // Key end and Value start.
+ mark("kV");
+
+ // Update the digest with the value.
+ update(value);
+
+ // Value end.
+ mark("v");
+ }
+
+
+ private boolean shouldIgnore(JsonNode value)
+ {
+ if (ignoreNull && value.isNull())
+ {
+ // The value of the field is null. Ignore this entry.
+ return true;
+ }
+
+ if (ignoreFalse && value.isBoolean())
+ {
+ // Ignore this entry if its value is false.
+ return value.asBoolean() == false;
+ }
+
+ if (ignoreZero && value.isNumber())
+ {
+ // Ignore this entry if its value is zero.
+ return isZero(value);
+ }
+
+ if (ignoreEmptyString && value.isTextual())
+ {
+ // Ignore this entry if its value is an empty string.
+ return value.textValue().length() == 0;
+ }
+
+ if (ignoreEmptyArray && value.isArray())
+ {
+ // Ignore this entry if its value is an empty array.
+ return value.size() == 0;
+ }
+
+ if (ignoreEmptyObject && value.isObject())
+ {
+ // Ignore this entry if its value is an empty object.
+ return value.size() == 0;
+ }
+
+ // Should not ignore.
+ return false;
+ }
+
+
+ private boolean isZero(JsonNode value)
+ {
+ // int
+ if (value.isInt() && value.intValue() == 0)
+ {
+ return true;
+ }
+
+ // long
+ if (value.isLong() && value.longValue() == 0)
+ {
+ return true;
+ }
+
+ // short
+ if (value.isShort() && value.shortValue() == 0)
+ {
+ return true;
+ }
+
+ // float
+ if (value.isFloat() && value.floatValue() == 0.0F)
+ {
+ return true;
+ }
+
+ // double
+ if (value.isDouble() && value.doubleValue() == 0.0)
+ {
+ return true;
+ }
+
+ // BigInteger
+ if (value.isBigInteger() && value.bigIntegerValue().equals(BigInteger.ZERO))
+ {
+ return true;
+ }
+
+ // BigDecimal
+ if (value.isBigDecimal() && value.decimalValue().equals(BigDecimal.ZERO))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+
+ private List getFieldNames(JsonNode node)
+ {
+ // Generate a list of field names.
+ List list = iteratorToList(node.fieldNames());
+
+ if (sortKeys)
+ {
+ // Sort on field names.
+ Collections.sort(list);
+ }
+
+ return list;
+ }
+
+
+ private List iteratorToList(Iterator iterator)
+ {
+ List list = new ArrayList();
+
+ while (iterator.hasNext())
+ {
+ list.add(iterator.next());
+ }
+
+ return list;
+ }
+
+
+ private void updateBoolean(JsonNode node)
+ {
+ // Boolean start.
+ mark("B");
+
+ digest.update(node.asBoolean());
+
+ // "Boolean end.
+ mark("b");
+ }
+
+
+ private void updateNumber(JsonNode node)
+ {
+ // Number start.
+ mark("N");
+
+ // Numbers contribute to the digest as String to make it
+ // easy to implement JsonDigestUpdater equivalent for other
+ // non-Java platforms.
+ //
+ // Note that BigDecimal.toString() is used for floating point
+ // numbers because USE_BIG_DECIMAL_FOR_FLOATS is enabled.
+ digest.update(node.toString());
+
+ // Number end.
+ mark("n");
+ }
+
+
+ private void updateText(JsonNode node)
+ {
+ // Text start.
+ mark("T");
+
+ digest.update(node.asText());
+
+ // Text end.
+ mark("t");
+ }
+
+
+ private void updateNull(JsonNode node)
+ {
+ // Null start.
+ mark("<");
+
+ digest.update("NULL");
+
+ // Null end.
+ mark(">");
+ }
+
+
+ private void updateBinary(JsonNode node)
+ {
+ // Binary start.
+ mark("[");
+
+ try
+ {
+ digest.update(node.binaryValue());
+ }
+ catch (IOException e)
+ {
+ // This won't happen because updateBinary(JsonNode)
+ // is called only when node.isBinary() returns true.
+ }
+
+ // Binary end.
+ mark("]");
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/authlete/common/util/security/AESCipher.java b/src/main/java/com/authlete/common/util/security/AESCipher.java
new file mode 100644
index 000000000..9e770a70a
--- /dev/null
+++ b/src/main/java/com/authlete/common/util/security/AESCipher.java
@@ -0,0 +1,554 @@
+package com.authlete.common.util.security;
+
+
+import static com.authlete.common.util.security.StandardCipherTransformations.AES_CBC_PKCS5PADDING;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import org.apache.commons.codec.BinaryDecoder;
+import org.apache.commons.codec.BinaryEncoder;
+import org.apache.commons.codec.binary.Base64;
+
+
+/**
+ * Cipher using {@code "AES/CBC/PKCS5Padding"}.
+ *
+ *
+ * // 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 AESCipher(TCoder coder)
+ {
+ super(DEFAULT_TRANSFORMATION, coder);
+ }
+
+
+ /**
+ * Constructor with a transformation and a coder.
+ *
+ *
+ * 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 AESCipher(String transformation, TCoder coder)
+ {
+ super(transformation, coder);
+ }
+
+
+ /**
+ * Set cipher initialization parameters.
+ *
+ *
+ * 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}).
+ *
+ *
+ * @param key
+ * Secret key.
+ *
+ * @param iv
+ * Initial vector.
+ *
+ * @return
+ * {@code this} object.
+ */
+ public AESCipher setKey(byte[] key, byte[] iv)
+ {
+ return setKey(key, iv, DEFAULT_KEY_SIZE);
+ }
+
+
+ /**
+ * Set cipher initialization parameters. Other {@code setKey}
+ * method variants except {@link #setKey(SecretKey, IvParameterSpec)}
+ * eventually call this method.
+ *
+ *
+ * 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}).
+ *
+ *
+ * @param key
+ * Secret key.
+ *
+ * @param iv
+ * Initial vector.
+ *
+ * @return
+ * {@code this} object.
+ */
+ public AESCipher setKey(String key, byte[] iv)
+ {
+ return setKey(key, iv, DEFAULT_KEY_SIZE);
+ }
+
+
+ /**
+ * Set cipher initialization parameters.
+ *
+ *
+ * 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}).
+ *
+ *
+ * @param key
+ * Secret key.
+ *
+ * @param iv
+ * Initial vector.
+ *
+ * @return
+ * {@code this} object.
+ */
+ public AESCipher setKey(String key, String iv)
+ {
+ return setKey(key, iv, DEFAULT_KEY_SIZE);
+ }
+
+
+ /**
+ * Set cipher initialization parameters.
+ *
+ *
+ * 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})
+ *
+ *
+ * @param key
+ * Secret key.
+ *
+ * @return
+ * {@code this} object.
+ */
+ public AESCipher setKey(String key)
+ {
+ return setKey(key, DEFAULT_KEY_SIZE);
+ }
+
+
+ /**
+ * Set cipher initialization parameters.
+ *
+ *
+ * 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}).
+ *
+ *
+ * @param key
+ * Secret key.
+ *
+ * @param iv
+ * Initial vector.
+ *
+ * @return
+ * {@code this} object.
+ */
+ public AESCipher setKey(byte[] key, String iv)
+ {
+ return setKey(key, iv, DEFAULT_KEY_SIZE);
+ }
+
+
+ /**
+ * Set cipher initialization parameters.
+ *
+ *
+ * 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}).
+ *
+ *
+ * @param key
+ * Secret key.
+ *
+ * @return
+ * {@code this} object.
+ */
+ public AESCipher setKey(byte[] key)
+ {
+ return setKey(key, DEFAULT_KEY_SIZE);
+ }
+
+
+ /**
+ * Set cipher initialization parameters.
+ *
+ *
+ * This method is an alias of {@link #setKey(byte[], byte[], int)
+ * setKey}(key, (byte[])null, keySize).
+ *
+ *
+ * @param key
+ * Secret key.
+ *
+ * @param keySize
+ * The size of the secret key in bytes.
+ *
+ * @return
+ * {@code this} object.
+ */
+ public AESCipher setKey(byte[] key, int keySize)
+ {
+ return setKey(key, (byte[])null, keySize);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/authlete/common/util/security/CodecCipher.java b/src/main/java/com/authlete/common/util/security/CodecCipher.java
new file mode 100644
index 000000000..06739d758
--- /dev/null
+++ b/src/main/java/com/authlete/common/util/security/CodecCipher.java
@@ -0,0 +1,1209 @@
+package com.authlete.common.util.security;
+
+
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.Provider;
+import java.security.SecureRandom;
+import java.security.cert.Certificate;
+import java.security.spec.AlgorithmParameterSpec;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import org.apache.commons.codec.BinaryDecoder;
+import org.apache.commons.codec.BinaryEncoder;
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.EncoderException;
+import org.apache.commons.codec.binary.Base64;
+
+
+/**
+ * Cipher with encoder and decoder.
+ *
+ *
+ * // 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 CodecCipher(Cipher cipher, TCoder coder)
+ {
+ this(cipher, coder, coder);
+ }
+
+
+ /**
+ * Constructor with a cipher.
+ *
+ *
+ * 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 CodecCipher(String transformation, TCoder coder) throws IllegalArgumentException
+ {
+ this(transformation, coder, coder);
+ }
+
+
+ /**
+ * Constructor with a transformation.
+ *
+ *
+ * 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 CodecCipher(String transformation) throws IllegalArgumentException
+ {
+ this(transformation, (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 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 CodecCipher(String transformation, String provider, TCoder coder) throws IllegalArgumentException
+ {
+ this(transformation, provider, coder, coder);
+ }
+
+
+ /**
+ * Constructor with a transformation and a provider.
+ *
+ *
+ * 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 CodecCipher(String transformation, Provider provider, TCoder coder) throws IllegalArgumentException
+ {
+ this(transformation, provider, coder, coder);
+ }
+
+
+ /**
+ * Constructor with a transformation and a provider.
+ *
+ *
+ * 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 CodecCipher setCoder(TCoder coder)
+ {
+ this.encoder = coder;
+ this.decoder = coder;
+
+ return this;
+ }
+
+
+ /**
+ * Set cipher initialization parameters.
+ *
+ *
+ * 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.
+ *
+ * A cipher instance has not been set.
+ * {@code setInit} method has not been called.
+ * The cipher failed to encrypt the input (probably initialization
+ * parameters set by {@code setInit} method are wrong).
+ * The encoder failed to encode the encrypted byte array.
+ *
+ */
+ public String encrypt(String input) throws IllegalStateException
+ {
+ return cipher(input, Cipher.ENCRYPT_MODE);
+ }
+
+
+ /**
+ * Decrypt the given string.
+ *
+ * @param input
+ * Encrypted string encoded by an encoder.
+ *
+ * @return
+ * Decrypted plain text.
+ * If {@code input} is {@code null}, {@code null} is returned.
+ *
+ * @throws IllegalStateException
+ * Failed to decrypt the input. The {@code IllegalStateException}
+ * wraps the original exception (such as {@link InvalidKeyException}
+ * and {@link DecoderException}) as the cause, if any.
+ *
+ * A cipher instance has not been set.
+ * {@code setInit} method has not been called.
+ * The decoder failed to decode the input.
+ * The cipher failed to decrypt the byte array (probably initialization
+ * parameters set by {@code setInit} method are wrong).
+ *
+ */
+ public String decrypt(String input) throws IllegalStateException
+ {
+ return cipher(input, Cipher.DECRYPT_MODE);
+ }
+
+
+ /**
+ * Encrypt the given byte array.
+ *
+ * @param input
+ * Plain byte array before encryption.
+ *
+ * @return
+ * Encrypted byte array 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.
+ *
+ * A cipher instance has not been set.
+ * {@code setInit} method has not been called.
+ * The cipher failed to encrypt the input (probably initialization
+ * parameters set by {@code setInit} method are wrong).
+ * The encoder failed to encode the encrypted byte array.
+ *
+ */
+ public byte[] encrypt(byte[] input) throws IllegalStateException
+ {
+ return cipher(input, Cipher.ENCRYPT_MODE);
+ }
+
+
+ /**
+ * Decrypt the given byte array.
+ *
+ * @param input
+ * Encrypted byte array encoded by an encoder.
+ *
+ * @return
+ * Decrypted plain byte array.
+ * If {@code input} is {@code null}, {@code null} is returned.
+ *
+ * @throws IllegalStateException
+ * Failed to decrypt the input. The {@code IllegalStateException}
+ * wraps the original exception (such as {@link InvalidKeyException}
+ * and {@link DecoderException}) as the cause, if any.
+ *
+ * A cipher instance has not been set.
+ * {@code setInit} method has not been called.
+ * The decoder failed to decode the input.
+ * The cipher failed to decrypt the byte array (probably initialization
+ * parameters set by {@code setInit} method are wrong).
+ *
+ */
+ public byte[] decrypt(byte[] input) throws IllegalStateException
+ {
+ return cipher(input, Cipher.DECRYPT_MODE);
+ }
+
+
+ /**
+ * Encrypt or decrypt.
+ */
+ private String cipher(String input, int mode)
+ {
+ if (input == null)
+ {
+ return null;
+ }
+
+ // Convert the input string into a byte array.
+ byte[] inputBytes = Utils.getBytesUTF8(input);
+
+ // Encrypt or decrypt.
+ byte[] outputBytes = cipher(inputBytes, mode);
+
+ // Build a string from the byte array.
+ return Utils.toStringUTF8(outputBytes);
+ }
+
+
+ /**
+ * Encrypt or decrypt.
+ */
+ private byte[] cipher(byte[] input, int mode)
+ {
+ try
+ {
+ return doCipher(input, mode);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalStateException(e);
+ }
+ }
+
+
+ /**
+ * Encrypt or decrypt.
+ */
+ private byte[] doCipher(byte[] input, int mode) throws
+ DecoderException, InvalidKeyException, InvalidAlgorithmParameterException,
+ IllegalBlockSizeException, BadPaddingException, EncoderException
+ {
+ if (input == null)
+ {
+ return null;
+ }
+
+ if (cipher == null)
+ {
+ throw new IllegalStateException("setCipher() has not been called.");
+ }
+
+ if (key == null && certificate == null)
+ {
+ throw new IllegalStateException("setInit() has not been called.");
+ }
+
+ if (mode == Cipher.DECRYPT_MODE)
+ {
+ input = decode(input);
+ }
+
+ initCipher(mode);
+
+ byte[] output = cipher.doFinal(input);
+
+ if (mode == Cipher.ENCRYPT_MODE)
+ {
+ output = encode(output);
+ }
+
+ return output;
+ }
+
+
+ /**
+ * Initialize {@link cipher} by calling one of {@code Cipher.init} methods
+ * using {@code mode} and one or some of {@link #key}, {@link #params},
+ * {@link #spec}, {@link #certificate} and {@link #random}.
+ */
+ private void initCipher(int mode) throws InvalidKeyException, InvalidAlgorithmParameterException
+ {
+ if (key != null)
+ {
+ if (params != null)
+ {
+ if (random != null)
+ {
+ cipher.init(mode, key, params, random);
+ }
+ else
+ {
+ cipher.init(mode, key, params);
+ }
+ }
+ else if (spec != null)
+ {
+ if (random != null)
+ {
+ cipher.init(mode, key, spec, random);
+ }
+ else
+ {
+ cipher.init(mode, key, spec);
+ }
+ }
+ else
+ {
+ if (random != null)
+ {
+ cipher.init(mode, key, random);
+ }
+ else
+ {
+ cipher.init(mode, key);
+ }
+ }
+ }
+ else
+ {
+ if (random != null)
+ {
+ cipher.init(mode, certificate, random);
+ }
+ else
+ {
+ cipher.init(mode, certificate);
+ }
+ }
+ }
+
+
+ /**
+ * Encode the byte array using {@link #encoder}, or using
+ * {@link #DEFAULT_CODER} if {@code encoder} is {@code null}.
+ */
+ private byte[] encode(byte[] input) throws EncoderException
+ {
+ if (encoder != null)
+ {
+ return encoder.encode(input);
+ }
+ else
+ {
+ return DEFAULT_CODER.encode(input);
+ }
+ }
+
+
+ /**
+ * Decode the byte array using {@link #decoder}, or using
+ * {@link #DEFAULT_CODER} if {@code decoder} is {@code null}.
+ */
+ private byte[] decode(byte[] input) throws DecoderException
+ {
+ if (decoder != null)
+ {
+ return decoder.decode(input);
+ }
+ else
+ {
+ return DEFAULT_CODER.decode(input);
+ }
+ }
+}
diff --git a/src/main/java/com/authlete/common/util/security/StandardCipherTransformations.java b/src/main/java/com/authlete/common/util/security/StandardCipherTransformations.java
new file mode 100644
index 000000000..1489f5ae0
--- /dev/null
+++ b/src/main/java/com/authlete/common/util/security/StandardCipherTransformations.java
@@ -0,0 +1,114 @@
+package com.authlete.common.util.security;
+
+
+/**
+ * List of standard cipher transformations which are valid as
+ * arguments for {@link javax.crypto.Cipher#getInstance(String)}.
+ *
+ *
+ * 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 000000000..9977d45a9
--- /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/assurance/VerifiedClaimsTest.java b/src/test/java/com/authlete/common/assurance/VerifiedClaimsTest.java
index 6cdd7dedb..1d5defac9 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 a4f1263f7..bd271178e 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 3210ed2af..0db0d215a 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/ida/DatasetExtractorTest.java b/src/test/java/com/authlete/common/ida/DatasetExtractorTest.java
index 6416a46d8..0a33c00de 100644
--- a/src/test/java/com/authlete/common/ida/DatasetExtractorTest.java
+++ b/src/test/java/com/authlete/common/ida/DatasetExtractorTest.java
@@ -16,11 +16,11 @@
package com.authlete.common.ida;
+import com.authlete.common.util.digest.Digest;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
-import com.neovisionaries.security.Digest;
import static org.junit.Assert.*;
import java.io.IOException;
import java.io.InputStreamReader;
From 4ed4f6c6ca8e6a245f087e9c57d979b00f7e6677 Mon Sep 17 00:00:00 2001
From: Kyle Gonzalez
Date: Wed, 1 Oct 2025 11:36:30 +0900
Subject: [PATCH 012/132] - Add new "inScopeForTokenMigration" flag in the
Client object.
---
.../java/com/authlete/common/dto/Client.java | 40 +++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/src/main/java/com/authlete/common/dto/Client.java b/src/main/java/com/authlete/common/dto/Client.java
index 63ca8992f..418f9559c 100644
--- a/src/main/java/com/authlete/common/dto/Client.java
+++ b/src/main/java/com/authlete/common/dto/Client.java
@@ -583,6 +583,13 @@ public class Client implements Serializable
*/
private boolean mtlsEndpointAliasesUsed;
+ /**
+ * Indicates if the client is inscope for token migration.
+ *
+ * @since Authlete 4.23
+ */
+ private boolean inScopeForTokenMigration;
+
/*
* For OpenID Federation 1.0.
@@ -5209,6 +5216,39 @@ public Client setMtlsEndpointAliasesUsed(boolean mtlsEndpointAliasesUsed)
}
+ /**
+ * Get the value that indicates whether this Client is in scope 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 a {@code Map} instance that represents a set of standard client
* metadata.
From c42d8bb7f567a20988355bec89ad3b5cee59774b Mon Sep 17 00:00:00 2001
From: meysam
Date: Thu, 2 Oct 2025 14:22:03 +0400
Subject: [PATCH 013/132] feat: add response headers support to ApiResponse
- Added responseHeaders field to store HTTP response headers
- Added getter/setter methods with proper JavaDoc
- Updated CHANGES.md for version 4.23
This enables downstream libraries to extract and provide access to
HTTP response headers from Authlete API calls, particularly the
Request-Id header requested by customers.
---
CHANGES.md | 9 ++++++
.../com/authlete/common/dto/ApiResponse.java | 32 +++++++++++++++++++
2 files changed, 41 insertions(+)
diff --git a/CHANGES.md b/CHANGES.md
index b60a19191..014acd9d4 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,15 @@
CHANGES
=======
+4.23 (TBD)
+----------
+
+- `ApiResponse` class
+ * Added the `getResponseHeaders()` method.
+ * Added the `setResponseHeaders(Map>)` method.
+ * Added support for extracting HTTP response headers from Authlete API calls.
+
+
4.22 (2025-08-14)
-----------------
diff --git a/src/main/java/com/authlete/common/dto/ApiResponse.java b/src/main/java/com/authlete/common/dto/ApiResponse.java
index 434936855..238bc2c9b 100644
--- a/src/main/java/com/authlete/common/dto/ApiResponse.java
+++ b/src/main/java/com/authlete/common/dto/ApiResponse.java
@@ -17,6 +17,8 @@
import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
/**
@@ -32,6 +34,7 @@ public class ApiResponse implements Serializable
private String resultCode;
private String resultMessage;
+ private Map> responseHeaders;
/**
@@ -81,4 +84,33 @@ public void setResultMessage(String message)
{
this.resultMessage = message;
}
+
+
+ /**
+ * Get the HTTP response headers returned from an Authlete API call.
+ *
+ * @return
+ * A map of HTTP response headers. May be {@code null} or empty.
+ *
+ * @since 4.23
+ */
+ public Map> getResponseHeaders()
+ {
+ return responseHeaders;
+ }
+
+
+ /**
+ * Set the HTTP response headers returned from an Authlete API call.
+ *
+ * @param responseHeaders
+ * A map of HTTP response headers where each key is a header name
+ * and the corresponding value is a list of header values.
+ *
+ * @since 4.23
+ */
+ public void setResponseHeaders(Map> responseHeaders)
+ {
+ this.responseHeaders = responseHeaders;
+ }
}
From 87475b8a8fa4e22444c2f658a751bad42733d07c Mon Sep 17 00:00:00 2001
From: Kyle Gonzalez
Date: Fri, 3 Oct 2025 09:57:55 +0900
Subject: [PATCH 014/132] Update changes.md.
---
CHANGES.md | 3 +++
1 file changed, 3 insertions(+)
diff --git a/CHANGES.md b/CHANGES.md
index 014acd9d4..de04048a9 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -9,6 +9,9 @@ CHANGES
* Added the `setResponseHeaders(Map>)` method.
* Added support for extracting HTTP response headers from Authlete API calls.
+- `Client` class
+ * Added the `isInScopeForTokenMigration()` method.
+ * Added the `setInScopeForTokenMigration(boolean)` method.
4.22 (2025-08-14)
-----------------
From a0d0851ddfb2e8a5e24f73bcc0320b73edd52c66 Mon Sep 17 00:00:00 2001
From: Kyle Gonzalez
Date: Fri, 3 Oct 2025 12:08:36 +0900
Subject: [PATCH 015/132] Update changes markdown and package-info version
comment.
---
CHANGES.ja.md | 12 ++++++++++++
CHANGES.md | 2 +-
src/main/java/com/authlete/common/package-info.java | 4 ++--
3 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index be801636e..3231afedb 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,18 @@
変更点
======
+4.23 (2025 年 10 月 03 日)
+----------
+
+- `ApiResponse` クラス
+ * `getResponseHeaders()` メソッドを追加。
+ * `setResponseHeaders(Map>)` メソッドを追加。
+ * AuthleteのAPI呼び出しからHTTPレスポンスヘッダーを抽出するサポートを追加しました。
+
+- `Client` クラス
+ * `isInScopeForTokenMigration()` メソッドを追加。
+ * `setInScopeForTokenMigration(boolean)` メソッドを追加。
+
4.22 (2025 年 08 月 14 日)
-----------------
diff --git a/CHANGES.md b/CHANGES.md
index de04048a9..75a5237c1 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,7 +1,7 @@
CHANGES
=======
-4.23 (TBD)
+4.23 (2025-10-03)
----------
- `ApiResponse` class
diff --git a/src/main/java/com/authlete/common/package-info.java b/src/main/java/com/authlete/common/package-info.java
index feadfb82e..ca6e225b9 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.22</version>
+ * <version> 4.23</version>
* </dependency>
*
- * @version 4.22
+ * @version 4.23
*/
package com.authlete.common;
From 798143ccf234b868c651e14e699336c1459a75e3 Mon Sep 17 00:00:00 2001
From: Kyle Gonzalez
Date: Fri, 3 Oct 2025 12:15:38 +0900
Subject: [PATCH 016/132] Update doc.
---
CHANGES.ja.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index 3231afedb..81e2fe247 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -7,7 +7,7 @@
- `ApiResponse` クラス
* `getResponseHeaders()` メソッドを追加。
* `setResponseHeaders(Map>)` メソッドを追加。
- * AuthleteのAPI呼び出しからHTTPレスポンスヘッダーを抽出するサポートを追加しました。
+ * AuthleteのAPI呼び出しからHTTPレスポンスヘッダーを抽出するサポートを追加。
- `Client` クラス
* `isInScopeForTokenMigration()` メソッドを追加。
From 6d7dc6f5dba80f6734e5ba198da3464575f95d71 Mon Sep 17 00:00:00 2001
From: Hideki Ikeda
Date: Fri, 3 Oct 2025 12:19:18 +0900
Subject: [PATCH 017/132] Revise HTTP response header extraction support
wording in CHANGE.ja.md
Updated the description for extracting HTTP response headers from Authlete API calls in CHANGES.ja.md
---
CHANGES.ja.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index 81e2fe247..a3cdffda9 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -7,7 +7,7 @@
- `ApiResponse` クラス
* `getResponseHeaders()` メソッドを追加。
* `setResponseHeaders(Map>)` メソッドを追加。
- * AuthleteのAPI呼び出しからHTTPレスポンスヘッダーを抽出するサポートを追加。
+ * Authlete の API 呼び出しから HTTP レスポンスヘッダーを取り出す機能をサポート。
- `Client` クラス
* `isInScopeForTokenMigration()` メソッドを追加。
From 3820600985c818be2e8855d2e12d077311b2f782 Mon Sep 17 00:00:00 2001
From: Hideki Ikeda
Date: Fri, 3 Oct 2025 12:31:27 +0900
Subject: [PATCH 018/132] Added information about "removing nv-cipher and
nv-digest dependencies" to CHANGE.md.
---
CHANGES.md | 3 +++
1 file changed, 3 insertions(+)
diff --git a/CHANGES.md b/CHANGES.md
index 75a5237c1..1cf41535c 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -12,6 +12,9 @@ CHANGES
- `Client` class
* Added the `isInScopeForTokenMigration()` method.
* Added the `setInScopeForTokenMigration(boolean)` method.
+
+- Removed `com.neovisionaries:nv-cipher` and `com.neovisionaries:nv-digest` dependencies.
+
4.22 (2025-08-14)
-----------------
From 852efbbd8e9856aec9e25b45a33226f249444a6e Mon Sep 17 00:00:00 2001
From: Hideki Ikeda
Date: Fri, 3 Oct 2025 12:38:16 +0900
Subject: [PATCH 019/132] Added information about "removing nv-cipher and
nv-digest dependencies" to CHANGE.ja.md.
---
CHANGES.ja.md | 3 +++
1 file changed, 3 insertions(+)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index a3cdffda9..17d0a09c0 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -13,6 +13,9 @@
* `isInScopeForTokenMigration()` メソッドを追加。
* `setInScopeForTokenMigration(boolean)` メソッドを追加。
+- `com.neovisionaries:nv-cipher` と `com.neovisionaries:nv-digest` を依存関係から削除。
+
+
4.22 (2025 年 08 月 14 日)
-----------------
From 8fb912aa94adfad06bebe127ab6fdfa7db78692f Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Fri, 3 Oct 2025 05:20:21 +0000
Subject: [PATCH 020/132] [maven-release-plugin] prepare release
authlete-java-common-4.23
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index b21d15c70..08e86fee5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.23-SNAPSHOT
+ 4.23
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.21
+ authlete-java-common-4.23
From 165666de04e1328300cd0ac2e384763921f545f2 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Fri, 3 Oct 2025 05:20:22 +0000
Subject: [PATCH 021/132] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 08e86fee5..c3d73c51c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.23
+ 4.24-SNAPSHOT
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.23
+ authlete-java-common-4.21
From aed7d2b7561ea9bcad7b7eb0973a1d0af4876a5a Mon Sep 17 00:00:00 2001
From: Zulfiqar Ahmed
Date: Mon, 6 Oct 2025 15:28:46 +0500
Subject: [PATCH 022/132] feat: add additionalClaims field to TokenResponse for
Native SSO
Add additionalClaims field to return any claims that need to be added
to the ID token when action is NATIVE_SSO. This field contains a JSON
string with claims like nonce and s_hash.
---
.../authlete/common/dto/TokenResponse.java | 44 +++++++++++++++++++
1 file changed, 44 insertions(+)
diff --git a/src/main/java/com/authlete/common/dto/TokenResponse.java b/src/main/java/com/authlete/common/dto/TokenResponse.java
index 7a1584174..7b1a86c71 100644
--- a/src/main/java/com/authlete/common/dto/TokenResponse.java
+++ b/src/main/java/com/authlete/common/dto/TokenResponse.java
@@ -1598,6 +1598,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.
*
@@ -3556,6 +3562,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
Date: Tue, 7 Oct 2025 13:44:50 +0500
Subject: [PATCH 023/132] update change log
---
CHANGES.ja.md | 9 +++++++++
CHANGES.md | 9 +++++++++
2 files changed, 18 insertions(+)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index 17d0a09c0..e4947cb84 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,15 @@
変更点
======
+4.24 (2025 年 10 月 06 日)
+----------
+
+- `TokenResponse` クラス
+ * `getAdditionalClaims()` メソッドを追加
+ * `setAdditionalClaims(String)` メソッドを追加
+ * カスタムクレームを処理するために Native SSO のトークンレスポンスに追加されたクレームへのアクセスをサポート
+
+
4.23 (2025 年 10 月 03 日)
----------
diff --git a/CHANGES.md b/CHANGES.md
index 1cf41535c..3de534f6e 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,15 @@
CHANGES
=======
+4.24 (2025-10-06)
+-----------------
+
+- `TokenResponse` class
+ * Added the `getAdditionalClaims()` method.
+ * Added the `setAdditionalClaims(String)` method.
+ * Enables access to additional claims data in Native SSO token responses for custom claim handling
+
+
4.23 (2025-10-03)
----------
From 040439fa85c17d76562271df502af74f9842c714 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Tue, 7 Oct 2025 09:54:02 +0000
Subject: [PATCH 024/132] [maven-release-plugin] prepare release
authlete-java-common-4.24
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index c3d73c51c..79b8552d9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.24-SNAPSHOT
+ 4.24
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.21
+ authlete-java-common-4.24
From 4c918555567485151e5b91c1088eccdbf1096d7d Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Tue, 7 Oct 2025 09:54:04 +0000
Subject: [PATCH 025/132] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 79b8552d9..8ff0b8d5e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.24
+ 4.25-SNAPSHOT
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.24
+ authlete-java-common-4.21
From bd73b975088021a11de5a37d81b4501e089242ae Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Thu, 30 Oct 2025 23:08:35 +0900
Subject: [PATCH 026/132] feat: Add an oid4vciVersion property to the Service
class (#144)
---
CHANGES.ja.md | 5 +
CHANGES.md | 5 +
.../java/com/authlete/common/dto/Service.java | 123 +++++++++++++++++-
3 files changed, 132 insertions(+), 1 deletion(-)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index e4947cb84..6c17be0c7 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,11 @@
変更点
======
+- `Service` クラス
+ * `getOid4vciVersion()` メソッドを追加。
+ * `setOid4vciVersion(String)` メソッドを追加。
+
+
4.24 (2025 年 10 月 06 日)
----------
diff --git a/CHANGES.md b/CHANGES.md
index 3de534f6e..b0a6d0040 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,11 @@
CHANGES
=======
+- `Service` class
+ * Added the `getOid4vciVersion()` method.
+ * Added the `setOid4vciVersion(String)` method.
+
+
4.24 (2025-10-06)
-----------------
diff --git a/src/main/java/com/authlete/common/dto/Service.java b/src/main/java/com/authlete/common/dto/Service.java
index 3ffdf3949..95b84760d 100644
--- a/src/main/java/com/authlete/common/dto/Service.java
+++ b/src/main/java/com/authlete/common/dto/Service.java
@@ -330,7 +330,7 @@
*/
public class Service implements Serializable
{
- private static final long serialVersionUID = 83L;
+ private static final long serialVersionUID = 84L;
/*
@@ -1883,6 +1883,16 @@ 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;
+
+
/**
* Get the service number.
*
@@ -11962,4 +11972,115 @@ 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.
+ *
+ *
+ * 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;
+ }
}
From 0dda6e74b55d18982f567cb3f37e0d9b5b2ae5eb Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Thu, 30 Oct 2025 23:10:07 +0900
Subject: [PATCH 027/132] Update CHANGES files for the version 4.25.
---
CHANGES.ja.md | 3 +++
CHANGES.md | 3 +++
2 files changed, 6 insertions(+)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index 6c17be0c7..aff4cb402 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,9 @@
変更点
======
+4.25 (2025 年 10 月 30 日)
+----------
+
- `Service` クラス
* `getOid4vciVersion()` メソッドを追加。
* `setOid4vciVersion(String)` メソッドを追加。
diff --git a/CHANGES.md b/CHANGES.md
index b0a6d0040..47d877d19 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,9 @@
CHANGES
=======
+4.25 (2025-10-30)
+-----------------
+
- `Service` class
* Added the `getOid4vciVersion()` method.
* Added the `setOid4vciVersion(String)` method.
From 6bd82ad80b5d46c6453d40378af95884aeb470ea Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Thu, 30 Oct 2025 14:28:46 +0000
Subject: [PATCH 028/132] [maven-release-plugin] prepare release
authlete-java-common-4.25
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 8ff0b8d5e..6bd240543 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.25-SNAPSHOT
+ 4.25
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.21
+ authlete-java-common-4.25
From 5737b1b1196748131f224a13dde88e225be4efff Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Thu, 30 Oct 2025 14:28:48 +0000
Subject: [PATCH 029/132] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 6bd240543..b3b4b6508 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.25
+ 4.26-SNAPSHOT
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.25
+ authlete-java-common-4.21
From 208d3c43185024030a3a62ce0cc1a6c2d0d2580b Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Mon, 3 Nov 2025 06:38:19 +0900
Subject: [PATCH 030/132] feat: OID4VCI 1.0 Final Metadata (#145)
---
CHANGES.ja.md | 24 +
CHANGES.md | 24 +
.../common/dto/CredentialIssuerMetadata.java | 792 +++++++++++++++++-
.../com/authlete/common/types/JWEZip.java | 189 +++++
.../dto/CredentialIssuerMetadataTest.java | 174 +++-
5 files changed, 1172 insertions(+), 31 deletions(-)
create mode 100644 src/main/java/com/authlete/common/types/JWEZip.java
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index aff4cb402..f504737dd 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,30 @@
変更点
======
+- `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 日)
----------
diff --git a/CHANGES.md b/CHANGES.md
index 47d877d19..82ee1c593 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,30 @@
CHANGES
=======
+- `CredentialIssuerMetadata` class
+ * Added the `getNotificationEndpoint()` method.
+ * Added the `setNotificationEndpoint(URI)` method.
+ * Added the `getNonceEndpoint()` method.
+ * Added the `setNonceEndpoint(URI)` method.
+ * Added the `getCredentialRequestEncryptionJwks()` method.
+ * Added the `setCredentialRequestEncryptionJwks(String)` method.
+ * Added the `getCredentialRequestEncryptionEncValuesSupported()` method.
+ * Added the `setCredentialRequestEncryptionEncValuesSupported(JWEEnc[])` method.
+ * Added the `getCredentialRequestEncryptionZipValuesSupported()` method.
+ * Added the `setCredentialRequestEncryptionZipValuesSupported(JWEEnc[])` method.
+ * Added the `isRequireCredentialRequestEncryption()` method.
+ * Added the `setRequireCredentialRequestEncryption(boolean)` method.
+ * Added the `getCredentialResponseEncryptionZipValuesSupported()` method.
+ * Added the `setCredentialResponseEncryptionZipValuesSupported(JWEEnc[])` method.
+ * Added the `getBatchSize()` method.
+ * Added the `setBatchSize(int)` method.
+ * Added the `getDisplay()` method.
+ * Added the `setDisplay(String)` method.
+
+- New types
+ * `JWEZip` enum
+
+
4.25 (2025-10-30)
-----------------
diff --git a/src/main/java/com/authlete/common/dto/CredentialIssuerMetadata.java b/src/main/java/com/authlete/common/dto/CredentialIssuerMetadata.java
index 0a002df1b..7b0fb3209 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:
+ *
*
*
- * {@code credential_issuer}
- * {@code authorization_servers}
- * {@code credential_endpoint}
- * {@code batch_credential_endpoint}
- * {@code deferred_credential_endpoint}
- * {@code credential_response_encryption}
- * {@code credential_configurations_supported}
+ * {@code credential_identifiers_supported}
+ * {@code signed_metadata}
*
*
*
@@ -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 toMap()
put(map, "credential_endpoint", credentialEndpoint, false);
put(map, "batch_credential_endpoint", batchCredentialEndpoint, false);
put(map, "deferred_credential_endpoint", deferredCredentialEndpoint, false);
+ put(map, "notification_endpoint", notificationEndpoint, false);
+ put(map, "nonce_endpoint", nonceEndpoint, false);
+ put(map, "credential_request_encryption", credentialRequestEncryption(), false);
put(map, "credential_response_encryption", credentialResponseEncryption(), false);
+ put(map, "batch_credential_issuance", batchCredentialIssuance(), false);
+ putDisplay(map); // display
+ putCredentialConfigurationsSupported(map); // credential_configurations_supported
+
+ return map;
+ }
+
+
+ private Map credentialRequestEncryption()
+ {
+ if (credentialRequestEncryptionJwks == null ||
+ credentialRequestEncryptionEncValuesSupported == null ||
+ credentialRequestEncryptionEncValuesSupported.length == 0)
+ {
+ return null;
+ }
+
+ Map map = new LinkedHashMap<>();
+
+ put(map, "jwks", credentialRequestEncryptionPublicJwks(), true);
+ put(map, "enc_values_supported", credentialRequestEncryptionEncValuesSupported, true);
+ put(map, "zip_values_supported", credentialRequestEncryptionZipValuesSupported, false);
+ put(map, "encryption_required", requireCredentialRequestEncryption, true);
+
+ return map;
+ }
+
+
+ private Map credentialRequestEncryptionPublicJwks()
+ {
+ if (credentialRequestEncryptionJwks == null)
+ {
+ return null;
+ }
+
+ JWKSet jwks;
try
{
- // Parse the value of 'credentialsSupported' as a JSON object.
- putJsonObject(map, "credential_configurations_supported", credentialsSupported, false);
+ // Parse the string as a JWK Set.
+ jwks = JWKSet.parse(credentialRequestEncryptionJwks);
}
catch (Exception cause)
{
throw new IllegalStateException(
- "The value of the 'credentialsSupported' property failed to be parsed as a JSON object.", cause);
+ "The value of the 'credentialRequestEncryptionJwks' property failed to be parsed as a JWK Set.", cause);
}
- return map;
+ // Public keys only.
+ return jwks.toJSONObject(true);
}
@@ -727,8 +1419,74 @@ private Map credentialResponseEncryption()
put(map, "alg_values_supported", credentialResponseEncryptionAlgValuesSupported, true);
put(map, "enc_values_supported", credentialResponseEncryptionEncValuesSupported, true);
+ put(map, "zip_values_supported", credentialResponseEncryptionZipValuesSupported, false);
put(map, "encryption_required", requireCredentialResponseEncryption, true);
return map;
}
+
+
+ private Map batchCredentialIssuance()
+ {
+ // OpenID for Verifiable Credential Issuance 1.0
+ // 12.2.4. Credential Issuer Metadata Parameters
+ //
+ // batch_credential_issuance:
+ //
+ // OPTIONAL. Object containing information about the Credential
+ // Issuer's support for issuance of multiple Credentials in a batch
+ // in the Credential Endpoint. The presence of this parameter means
+ // that the issuer supports more than one key proof in the proofs
+ // parameter in the Credential Request so can issue more than one
+ // Verifiable Credential for the same Credential Dataset in a
+ // single request/response.
+ //
+ // batch_size:
+ // REQUIRED. Integer value specifying the maximum array size
+ // for the proofs parameter in a Credential Request. It MUST
+ // be 2 or greater.
+ //
+ if (batchSize < 2)
+ {
+ return null;
+ }
+
+ // {
+ // "batch_size": batchSize
+ // }
+ Map value = new LinkedHashMap<>();
+ value.put("batch_size", batchSize);
+
+ return value;
+ }
+
+
+ private void putDisplay(Map map)
+ {
+ try
+ {
+ // Parse the value of 'display as a JSON array.
+ putJsonArray(map, "display", display, false);
+ }
+ catch (Exception cause)
+ {
+ throw new IllegalStateException(
+ "The value of the 'display' property failed to be parsed as a JSON array.", cause);
+ }
+ }
+
+
+ private void putCredentialConfigurationsSupported(Map map)
+ {
+ try
+ {
+ // Parse the value of 'credentialsSupported' as a JSON object.
+ putJsonObject(map, "credential_configurations_supported", credentialsSupported, false);
+ }
+ catch (Exception cause)
+ {
+ throw new IllegalStateException(
+ "The value of the 'credentialsSupported' property failed to be parsed as a JSON object.", cause);
+ }
+ }
}
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 000000000..512e75407
--- /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 set)
+ {
+ return sHelper.toBits(set);
+ }
+
+
+ public static JWEZip[] toArray(int bits)
+ {
+ return sHelper.toArray(bits);
+ }
+
+
+ public static EnumSet toSet(int bits)
+ {
+ return sHelper.toSet(bits);
+ }
+
+
+ public static EnumSet toSet(JWEZip[] array)
+ {
+ return sHelper.toSet(array);
+ }
+
+
+ private static class Helper extends EnumHelper
+ {
+ public Helper(JWEZip[] values)
+ {
+ super(JWEZip.class, values);
+ }
+
+
+ @Override
+ protected short getValue(JWEZip entry)
+ {
+ return entry.getValue();
+ }
+
+
+ @Override
+ protected JWEZip[] newArray(int size)
+ {
+ return new JWEZip[size];
+ }
+ }
+}
diff --git a/src/test/java/com/authlete/common/dto/CredentialIssuerMetadataTest.java b/src/test/java/com/authlete/common/dto/CredentialIssuerMetadataTest.java
index 39b755295..fc5eaf1f2 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 map = metadata.toMap();
+
+ // credential_request_encryption
+ Map encryption = (Map)map.get("credential_request_encryption");
+ assertNotNull(encryption);
+
+ // credential_request_encryption.jwks
+ Map publicJwks = (Map)encryption.get("jwks");
+
+ List publicKeys = JWKSet.parse(publicJwks).getKeys();
+ assertEquals(1, publicKeys.size());
+
+ for (JWK key : publicKeys)
+ {
+ assertFalse("Keys in 'jwks' must not be private keys.", key.isPrivate());
+ }
+
+ // credential_request_encryption.enc_values_supported
+ List encValues = (List)encryption.get("enc_values_supported");
+ assertNotNull(encValues);
+ assertEquals(1, encValues.size());
+ assertEquals("A128CBC-HS256", encValues.get(0));
+
+ // credential_request_encryption.zip_values_supported
+ List zipValues = (List)encryption.get("zip_values_supported");
+ assertNotNull(zipValues);
+ assertEquals(1, zipValues.size());
+ assertEquals("DEF", zipValues.get(0));
+
+ // credential_request_encryption.encrypition_required
+ assertEquals(Boolean.TRUE, encryption.get("encryption_required"));
+ }
+
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void test_toMap_response_encryption()
+ {
+ JWEAlg[] algs = new JWEAlg[] { JWEAlg.ECDH_ES };
+ JWEEnc[] encs = new JWEEnc[] { JWEEnc.A128CBC_HS256 };
+ JWEZip[] zips = new JWEZip[] { JWEZip.DEF };
CredentialIssuerMetadata metadata = new CredentialIssuerMetadata()
- .setRequireCredentialResponseEncryption(true)
- .setCredentialIssuer(issuer)
.setCredentialResponseEncryptionAlgValuesSupported(algs)
.setCredentialResponseEncryptionEncValuesSupported(encs)
+ .setCredentialResponseEncryptionZipValuesSupported(zips)
+ .setRequireCredentialResponseEncryption(true)
;
Map map = metadata.toMap();
- // credential_issuer
- assertEquals("https://example.com", map.get("credential_issuer"));
-
// credential_response_encryption
Map encryption = (Map)map.get("credential_response_encryption");
assertNotNull(encryption);
- // credential_response_encryption.encrypition_required
- assertEquals(Boolean.TRUE, encryption.get("encryption_required"));
-
// credential_response_encryption.alg_values_supported
List algValues = (List)encryption.get("alg_values_supported");
assertNotNull(algValues);
@@ -65,5 +124,92 @@ public void test_toMap()
assertNotNull(encValues);
assertEquals(1, encValues.size());
assertEquals("A128CBC-HS256", encValues.get(0));
+
+ // credential_response_encryption.zip_values_supported
+ List zipValues = (List)encryption.get("zip_values_supported");
+ assertNotNull(zipValues);
+ assertEquals(1, zipValues.size());
+ assertEquals("DEF", zipValues.get(0));
+
+ // credential_response_encryption.encrypition_required
+ assertEquals(Boolean.TRUE, encryption.get("encryption_required"));
+ }
+
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void test_toMap_batch_size_1()
+ {
+ CredentialIssuerMetadata metadata = new CredentialIssuerMetadata()
+ .setBatchSize(1)
+ ;
+
+ Map map = metadata.toMap();
+
+ // batch_credential_issuance
+ Map bci = (Map)map.get("batch_credential_issuance");
+ assertNull(bci);
+ }
+
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void test_toMap_batch_size_2()
+ {
+ CredentialIssuerMetadata metadata = new CredentialIssuerMetadata()
+ .setBatchSize(2)
+ ;
+
+ Map map = metadata.toMap();
+
+ // batch_credential_issuance
+ Map bci = (Map)map.get("batch_credential_issuance");
+ assertNotNull(bci);
+
+ // batch_credential_issuance.batch_size
+ Number bs = (Number)bci.get("batch_size");
+ assertNotNull(bs);
+ assertEquals(2, bs.intValue());
+ }
+
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void test_toMap_display()
+ {
+ String display =
+ "[\n"
+ + " {\n"
+ + " \"name\": \"Example University\",\n"
+ + " \"locale\": \"en-US\",\n"
+ + " \"logo\": {\n"
+ + " \"uri\": \"https://university.example.edu/public/logo.png\",\n"
+ + " \"alt_text\":\"a square logo of a university\"\n"
+ + " }\n"
+ + " },\n"
+ + " {\n"
+ + " \"name\": \"Example Université\",\n"
+ + " \"locale\": \"fr-FR\",\n"
+ + " \"logo\": {\n"
+ + " \"uri\": \"https://university.example.edu/public/logo.png\",\n"
+ + " \"alt_text\":\"Un logo universitaire carré\"\n"
+ + " }\n"
+ + " }\n"
+ + "]";
+
+ CredentialIssuerMetadata metadata = new CredentialIssuerMetadata()
+ .setDisplay(display)
+ ;
+
+ Map map = metadata.toMap();
+
+ // display
+ Object displayObject = map.get("display");
+ assertNotNull(displayObject);
+
+ assertTrue("The value of 'display' should be a list", displayObject instanceof List);
+ List displayList = (List)displayObject;
+
+ assertEquals(2, displayList.size());
}
}
From a85c462976c19e030d7bac3725e4bde6309dd3b7 Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Mon, 3 Nov 2025 06:42:10 +0900
Subject: [PATCH 031/132] Update documents for version 4.26.
---
CHANGES.ja.md | 13 ++++++++-----
CHANGES.md | 3 +++
README.ja.md | 2 --
README.md | 2 --
4 files changed, 11 insertions(+), 9 deletions(-)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index f504737dd..5602bda78 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,9 @@
変更点
======
+4.26 (2025 年 11 月 03 日)
+--------------------------
+
- `CredentialIssuerMetadata` クラス
* `getNotificationEndpoint()` メソッドを追加。
* `setNotificationEndpoint(URI)` メソッドを追加。
@@ -26,7 +29,7 @@
4.25 (2025 年 10 月 30 日)
-----------
+--------------------------
- `Service` クラス
* `getOid4vciVersion()` メソッドを追加。
@@ -34,7 +37,7 @@
4.24 (2025 年 10 月 06 日)
-----------
+--------------------------
- `TokenResponse` クラス
* `getAdditionalClaims()` メソッドを追加
@@ -43,7 +46,7 @@
4.23 (2025 年 10 月 03 日)
-----------
+--------------------------
- `ApiResponse` クラス
* `getResponseHeaders()` メソッドを追加。
@@ -58,13 +61,13 @@
4.22 (2025 年 08 月 14 日)
------------------
+--------------------------
- nimbus-jose-jwt を バージョン 10.0.2 に更新。
4.21 (2025 年 07 月 18 日)
------------------
+--------------------------
- Authlete API V3 における HTTP URLConnection のサポートを追加。
- checkstyle 用の設定ファイルを追加。
diff --git a/CHANGES.md b/CHANGES.md
index 82ee1c593..bb0449a07 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,9 @@
CHANGES
=======
+4.26 (2025-11-03)
+-----------------
+
- `CredentialIssuerMetadata` class
* Added the `getNotificationEndpoint()` method.
* Added the `setNotificationEndpoint(URI)` method.
diff --git a/README.ja.md b/README.ja.md
index c8ba4a363..383210c61 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -54,8 +54,6 @@ JavaDoc
https://authlete.github.io/authlete-java-common/
- https://authlete.github.io/authlete-java-common/index.html?overview-summary.html [FRAMES]
-
説明
----
diff --git a/README.md b/README.md
index 0fc3e0dd7..80ce218c6 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
-----------
From f73e08c7a09c33f52dd6b08bd9b9bf3166ab832d Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Sun, 2 Nov 2025 21:48:53 +0000
Subject: [PATCH 032/132] [maven-release-plugin] prepare release
authlete-java-common-4.26
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index b3b4b6508..606576aca 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.26-SNAPSHOT
+ 4.26
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.21
+ authlete-java-common-4.26
From 46c9c396c9be3d4d0994a44daec58018fc14c767 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Sun, 2 Nov 2025 21:48:55 +0000
Subject: [PATCH 033/132] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 606576aca..83e608071 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.26
+ 4.27-SNAPSHOT
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.26
+ authlete-java-common-4.21
From b8a7577d7d0ba3f57d5b9be34c73fd03e86d5090 Mon Sep 17 00:00:00 2001
From: kenji yoshida <6b656e6a69@gmail.com>
Date: Tue, 4 Nov 2025 12:44:31 +0900
Subject: [PATCH 034/132] fix spotbugs-annotations dependency (#143)
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 83e608071..5e94726c7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -133,7 +133,7 @@
com.github.spotbugs
spotbugs-annotations
- RELEASE
+ 4.9.6
compile
From bb7d607661ae21bfa548802c779059451eda752c Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Tue, 4 Nov 2025 13:10:07 +0900
Subject: [PATCH 035/132] feat: /vci/nonce API for OID4VCI 1.0 nonce endpoint
(#146)
---
CHANGES.ja.md | 9 +
CHANGES.md | 9 +
README.ja.md | 1 +
README.md | 1 +
.../com/authlete/common/api/AuthleteApi.java | 41 +++
.../authlete/common/api/AuthleteApiImpl.java | 20 +-
.../common/api/AuthleteApiImplV3.java | 15 +
.../common/dto/CredentialNonceRequest.java | 75 +++++
.../common/dto/CredentialNonceResponse.java | 283 ++++++++++++++++++
.../java/com/authlete/common/dto/Service.java | 76 ++++-
10 files changed, 517 insertions(+), 13 deletions(-)
create mode 100644 src/main/java/com/authlete/common/dto/CredentialNonceRequest.java
create mode 100644 src/main/java/com/authlete/common/dto/CredentialNonceResponse.java
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index 5602bda78..54647bcd1 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,15 @@
変更点
======
+- `AuthleteApi` インターフェース
+ * `credentialNonce(CredentialNonceRequest)` メソッドを追加。
+ * `credentialNonce(CredentialNonceRequest, Options)` メソッドを追加。
+
+- 新しい型
+ * `CredentialNonceRequest` クラス
+ * `CredentialNonceResponse` クラス
+
+
4.26 (2025 年 11 月 03 日)
--------------------------
diff --git a/CHANGES.md b/CHANGES.md
index bb0449a07..185d8c6ea 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,15 @@
CHANGES
=======
+- `AuthleteApi` interface
+ * Added the `credentialNonce(CredentialNonceRequest)` method.
+ * Added the `credentialNonce(CredentialNonceRequest, Options)` method.
+
+- New types
+ * `CredentialNonceRequest` class
+ * `CredentialNonceResponse` class
+
+
4.26 (2025-11-03)
-----------------
diff --git a/README.ja.md b/README.ja.md
index 383210c61..c4b6c09aa 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -362,6 +362,7 @@ settings.setReadTimeout(5000);
- `credentialBatchIssue(CredentialBatchIssueRequest)`
- `credentialDeferredParse(CredentialDeferredParseRequest)`
- `credentialDeferredIssue(CredentialDeferredIssueRequest)`
+ - `credentialNonce(CredentialNonceRequest)`
22. OpenID Connect Native SSO for Mobile Apps 1.0 に関するメソッド群
diff --git a/README.md b/README.md
index 80ce218c6..f298c74f6 100644
--- a/README.md
+++ b/README.md
@@ -373,6 +373,7 @@ 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
diff --git a/src/main/java/com/authlete/common/api/AuthleteApi.java b/src/main/java/com/authlete/common/api/AuthleteApi.java
index c29d26504..0b58e1459 100644
--- a/src/main/java/com/authlete/common/api/AuthleteApi.java
+++ b/src/main/java/com/authlete/common/api/AuthleteApi.java
@@ -58,6 +58,8 @@
import com.authlete.common.dto.CredentialIssuerMetadataResponse;
import com.authlete.common.dto.CredentialJwtIssuerMetadataRequest;
import com.authlete.common.dto.CredentialJwtIssuerMetadataResponse;
+import com.authlete.common.dto.CredentialNonceRequest;
+import com.authlete.common.dto.CredentialNonceResponse;
import com.authlete.common.dto.CredentialOfferCreateRequest;
import com.authlete.common.dto.CredentialOfferCreateResponse;
import com.authlete.common.dto.CredentialOfferInfoRequest;
@@ -4567,6 +4569,45 @@ CredentialDeferredIssueResponse credentialDeferredIssue(
CredentialDeferredIssueRequest request, Options options) throws AuthleteApiException;
+ /**
+ * Call Authlete's {@code /vci/nonce} API.
+ *
+ * @param request
+ * Request parameters passed to the API.
+ *
+ * @return
+ * Response from the API.
+ *
+ * @since 4.27
+ * @since Authlete 3.0.22
+ */
+ default CredentialNonceResponse credentialNonce(
+ CredentialNonceRequest request) throws AuthleteApiException
+ {
+ return credentialNonce(request, null);
+ }
+
+
+ /**
+ * Call Authlete's {@code /vci/nonce} API.
+ *
+ * @param request
+ * Request parameters passed to the API.
+ *
+ * @param options
+ * Request options.
+ *
+ * @return
+ * Response from the API.
+ *
+ * @since 4.27
+ * @since Authlete 3.0.22
+ */
+ CredentialNonceResponse credentialNonce(
+ CredentialNonceRequest request, Options options) throws AuthleteApiException;
+
+
+
/**
* Call Authlete's {@code /idtoken/reissue} API.
*
diff --git a/src/main/java/com/authlete/common/api/AuthleteApiImpl.java b/src/main/java/com/authlete/common/api/AuthleteApiImpl.java
index 518d357f6..3d8709d54 100644
--- a/src/main/java/com/authlete/common/api/AuthleteApiImpl.java
+++ b/src/main/java/com/authlete/common/api/AuthleteApiImpl.java
@@ -66,6 +66,8 @@
import com.authlete.common.dto.CredentialIssuerMetadataResponse;
import com.authlete.common.dto.CredentialJwtIssuerMetadataRequest;
import com.authlete.common.dto.CredentialJwtIssuerMetadataResponse;
+import com.authlete.common.dto.CredentialNonceRequest;
+import com.authlete.common.dto.CredentialNonceResponse;
import com.authlete.common.dto.CredentialOfferCreateRequest;
import com.authlete.common.dto.CredentialOfferCreateResponse;
import com.authlete.common.dto.CredentialOfferInfoRequest;
@@ -204,6 +206,7 @@ class AuthleteApiImpl extends AuthleteApiBasicImpl
private static final String FEDERATION_CONFIGURATION_API_PATH = "/api/federation/configuration";
private static final String FEDERATION_REGISTRATION_API_PATH = "/api/federation/registration";
private static final String VCI_METADATA_API_PATH = "/api/vci/metadata";
+ private static final String VCI_NONCE_API_PATH = "/api/vci/nonce";
private static final String VCI_JWT_ISSUER_API_PATH = "/api/vci/jwtissuer";
private static final String VCI_JWKS_API_PATH = "/api/vci/jwks";
private static final String VCI_OFFER_CREATE_API_PATH = "/api/vci/offer/create";
@@ -221,7 +224,7 @@ class AuthleteApiImpl extends AuthleteApiBasicImpl
private final String mServiceOwnerAuth;
private final String mServiceAuth;
-
+
/**
* The constructor with an instance of {@link AuthleteConfiguration}.
*
@@ -1704,6 +1707,21 @@ public CredentialDeferredIssueResponse credentialDeferredIssue(
}
+ @Override
+ public CredentialNonceResponse credentialNonce(
+ CredentialNonceRequest request, Options options) throws AuthleteApiException
+ {
+ // Note that the /vci/nonce API is not available in Authlete 2.x,
+ // so the executeApiCall below will throw an exception.
+
+ return executeApiCall(
+ new ServicePostApiCaller(
+ CredentialNonceResponse.class, request,
+ VCI_NONCE_API_PATH)
+ .setOptions(options));
+ }
+
+
@Override
public IDTokenReissueResponse idTokenReissue(
IDTokenReissueRequest request, Options options) throws AuthleteApiException
diff --git a/src/main/java/com/authlete/common/api/AuthleteApiImplV3.java b/src/main/java/com/authlete/common/api/AuthleteApiImplV3.java
index cdbaa66b8..cd34b2b86 100644
--- a/src/main/java/com/authlete/common/api/AuthleteApiImplV3.java
+++ b/src/main/java/com/authlete/common/api/AuthleteApiImplV3.java
@@ -67,6 +67,8 @@
import com.authlete.common.dto.CredentialIssuerMetadataResponse;
import com.authlete.common.dto.CredentialJwtIssuerMetadataRequest;
import com.authlete.common.dto.CredentialJwtIssuerMetadataResponse;
+import com.authlete.common.dto.CredentialNonceRequest;
+import com.authlete.common.dto.CredentialNonceResponse;
import com.authlete.common.dto.CredentialOfferCreateRequest;
import com.authlete.common.dto.CredentialOfferCreateResponse;
import com.authlete.common.dto.CredentialOfferInfoRequest;
@@ -207,6 +209,7 @@ public class AuthleteApiImplV3 extends AuthleteApiBasicImpl
private static final String VCI_JWKS_API_PATH = "/api/%d/vci/jwks";
private static final String VCI_JWT_ISSUER_API_PATH = "/api/%d/vci/jwtissuer";
private static final String VCI_METADATA_API_PATH = "/api/%d/vci/metadata";
+ private static final String VCI_NONCE_API_PATH = "/api/%d/vci/nonce";
private static final String VCI_OFFER_CREATE_API_PATH = "/api/%d/vci/offer/create";
private static final String VCI_OFFER_INFO_API_PATH = "/api/%d/vci/offer/info";
private static final String VCI_SINGLE_PARSE_API_PATH = "/api/%d/vci/single/parse";
@@ -1569,6 +1572,18 @@ public CredentialDeferredIssueResponse credentialDeferredIssue(
}
+ @Override
+ public CredentialNonceResponse credentialNonce(
+ CredentialNonceRequest request, Options options) throws AuthleteApiException
+ {
+ return executeApiCall(
+ new PostApiCaller(
+ CredentialNonceResponse.class, request,
+ VCI_NONCE_API_PATH, mServiceId)
+ .setOptions(options));
+ }
+
+
@Override
public IDTokenReissueResponse idTokenReissue(
IDTokenReissueRequest request, Options options) throws AuthleteApiException
diff --git a/src/main/java/com/authlete/common/dto/CredentialNonceRequest.java b/src/main/java/com/authlete/common/dto/CredentialNonceRequest.java
new file mode 100644
index 000000000..1e8687355
--- /dev/null
+++ b/src/main/java/com/authlete/common/dto/CredentialNonceRequest.java
@@ -0,0 +1,75 @@
+/*
+ * 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;
+
+
+/**
+ * Request to Authlete's {@code /vci/nonce} API.
+ *
+ *
+ * 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 000000000..4bd7c9107
--- /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.
+ *
+ *
+ *
+ * In this case, the implementation of the nonce endpoint of the credential
+ * issuer should return an HTTP response with the HTTP status code
+ * "{@code 404 Not Found}" and the content type "{@code application/json}".
+ * The message body (= error information in the JSON format) 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.
+ *
+ *
+ *
+ * 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.
+ *
+ *
+ *
+ * In this case, 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}". The message body (= error
+ * information in the JSON format) of the response has been prepared by Authlete's
+ * {@code /vci/nonce} API and it is available as the {@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/Service.java b/src/main/java/com/authlete/common/dto/Service.java
index 95b84760d..df13c50af 100644
--- a/src/main/java/com/authlete/common/dto/Service.java
+++ b/src/main/java/com/authlete/common/dto/Service.java
@@ -11102,13 +11102,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.
*
*
- * {@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}.
+ * 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.
+ *
+ *
+ *
+ * For {@code c_nonce} in 1.0-Final, this {@code cnonceDuration} property
+ * specifies the validity period (time window) in seconds.
*
*
*
@@ -11124,12 +11148,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
@@ -11144,13 +11170,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.
*
*
*
@@ -11166,12 +11216,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.
From 15b36ff11679277a156e733554dcdd1582d2b07c Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Tue, 4 Nov 2025 13:14:02 +0900
Subject: [PATCH 036/132] Update documents for version 4.27.
---
CHANGES.ja.md | 6 ++++++
CHANGES.md | 6 ++++++
2 files changed, 12 insertions(+)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index 54647bcd1..5d57ba9a8 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,9 @@
変更点
======
+4.27 (2025 年 11 月 04 日)
+--------------------------
+
- `AuthleteApi` インターフェース
* `credentialNonce(CredentialNonceRequest)` メソッドを追加。
* `credentialNonce(CredentialNonceRequest, Options)` メソッドを追加。
@@ -9,6 +12,9 @@
* `CredentialNonceRequest` クラス
* `CredentialNonceResponse` クラス
+- その他
+ * PR #143: fix spotbugs-annotations dependency
+
4.26 (2025 年 11 月 03 日)
--------------------------
diff --git a/CHANGES.md b/CHANGES.md
index 185d8c6ea..78c11ca23 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,9 @@
CHANGES
=======
+4.27 (2025-11-04)
+-----------------
+
- `AuthleteApi` interface
* Added the `credentialNonce(CredentialNonceRequest)` method.
* Added the `credentialNonce(CredentialNonceRequest, Options)` method.
@@ -9,6 +12,9 @@ CHANGES
* `CredentialNonceRequest` class
* `CredentialNonceResponse` class
+- Others
+ * PR #143: fix spotbugs-annotations dependency
+
4.26 (2025-11-03)
-----------------
From 6518550e757718337c0e7824880bc0fa6cc47c53 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Tue, 4 Nov 2025 04:17:23 +0000
Subject: [PATCH 037/132] [maven-release-plugin] prepare release
authlete-java-common-4.27
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 5e94726c7..505a8f948 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.27-SNAPSHOT
+ 4.27
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.21
+ authlete-java-common-4.27
From 999eee218c731ad193404e9daf8f222d559239c4 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Tue, 4 Nov 2025 04:17:25 +0000
Subject: [PATCH 038/132] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 505a8f948..7fae5a158 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.27
+ 4.28-SNAPSHOT
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.27
+ authlete-java-common-4.21
From 65c01113ac05b06001045c72e7a7d1bb7e4fc05e Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Wed, 5 Nov 2025 16:52:33 +0900
Subject: [PATCH 039/132] feat: Additional error codes for OID4VCI 1.0 Final
(#147)
---
CHANGES.ja.md | 9 ++
CHANGES.md | 9 ++
.../com/authlete/common/types/ErrorCode.java | 88 ++++++++++++++++++-
3 files changed, 104 insertions(+), 2 deletions(-)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index 5d57ba9a8..b66534dda 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,15 @@
変更点
======
+- `ErrorCode` 列挙型
+ * `unknown_credential_configuration` を追加。
+ * `unknown_credential_identifier` を追加。
+ * `invalid_nonce` を追加。
+ * `credential_request_denied` を追加。
+ * `invalid_notification_id` を追加。
+ * `invalid_notification_request` を追加。
+
+
4.27 (2025 年 11 月 04 日)
--------------------------
diff --git a/CHANGES.md b/CHANGES.md
index 78c11ca23..8e7115c18 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,15 @@
CHANGES
=======
+- `ErrorCode` enum
+ * Added `unknown_credential_configuration`
+ * Added `unknown_credential_identifier`
+ * Added `invalid_nonce`
+ * Added `credential_request_denied`
+ * Added `invalid_notification_id`
+ * Added `invalid_notification_request`
+
+
4.27 (2025-11-04)
-----------------
diff --git a/src/main/java/com/authlete/common/types/ErrorCode.java b/src/main/java/com/authlete/common/types/ErrorCode.java
index 0cb8f1345..2988d3314 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-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.
@@ -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,85 @@ 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,
;
}
From 82db2dfc48f2ff7bdb3e7676d6ffdad4ec34659f Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Wed, 5 Nov 2025 16:53:50 +0900
Subject: [PATCH 040/132] Updated CHANGES files for version 4.28.
---
CHANGES.ja.md | 3 +++
CHANGES.md | 3 +++
2 files changed, 6 insertions(+)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index b66534dda..497ea84e5 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,9 @@
変更点
======
+4.28 (2025 年 11 月 05 日)
+--------------------------
+
- `ErrorCode` 列挙型
* `unknown_credential_configuration` を追加。
* `unknown_credential_identifier` を追加。
diff --git a/CHANGES.md b/CHANGES.md
index 8e7115c18..07e066982 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,9 @@
CHANGES
=======
+4.28 (2025-11-05)
+-----------------
+
- `ErrorCode` enum
* Added `unknown_credential_configuration`
* Added `unknown_credential_identifier`
From bdd90ac43f065c894debb86fa30c7f2057fd794a Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Wed, 5 Nov 2025 07:57:38 +0000
Subject: [PATCH 041/132] [maven-release-plugin] prepare release
authlete-java-common-4.28
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 7fae5a158..b0a1d82b2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.28-SNAPSHOT
+ 4.28
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.21
+ authlete-java-common-4.28
From b1f3c00c7271fc04907feea5665043b35fe93232 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Wed, 5 Nov 2025 07:57:40 +0000
Subject: [PATCH 042/132] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index b0a1d82b2..0a468625a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.28
+ 4.29-SNAPSHOT
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.28
+ authlete-java-common-4.21
From d98d8676f8cea44d92c8a0dd90f0452b3758ed01 Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Tue, 18 Nov 2025 19:30:32 +0900
Subject: [PATCH 043/132] feat: OAuth Client ID Metadata Document (#148)
---
CHANGES.ja.md | 87 ++++
CHANGES.md | 87 ++++
.../common/dto/AuthorizationResponse.java | 76 +++-
...channelAuthenticationCompleteResponse.java | 151 ++++++-
.../BackchannelAuthenticationResponse.java | 151 ++++++-
.../java/com/authlete/common/dto/Client.java | 419 +++++++++++++++++-
.../dto/DeviceAuthorizationResponse.java | 149 ++++++-
.../dto/DeviceVerificationResponse.java | 151 ++++++-
.../common/dto/IntrospectionResponse.java | 117 ++++-
.../java/com/authlete/common/dto/Service.java | 68 ++-
.../common/dto/TokenCreateRequest.java | 90 +++-
.../com/authlete/common/dto/TokenInfo.java | 127 +++++-
.../common/dto/TokenIssueResponse.java | 114 ++++-
.../authlete/common/dto/TokenResponse.java | 112 ++++-
.../authlete/common/dto/UserInfoResponse.java | 117 ++++-
.../authlete/common/types/ClientSource.java | 128 ++++++
.../common/util/ClientMetadataControl.java | 58 ++-
17 files changed, 2138 insertions(+), 64 deletions(-)
create mode 100644 src/main/java/com/authlete/common/types/ClientSource.java
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index 497ea84e5..a47afeeba 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,93 @@
変更点
======
+- `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 日)
--------------------------
diff --git a/CHANGES.md b/CHANGES.md
index 07e066982..5d9235400 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,93 @@
CHANGES
=======
+- `AuthorizationResponse` class
+ * Added the `isMetadataDocumentUsed()` method.
+ * Added the `setMetadataDocumentUsed(boolean)` method.
+
+- `BackchannelAuthenticationCompleteResponse` class
+ * Added the `getMetadataDocumentLocation()` method.
+ * Added the `setMetadataDocumentLocation(URI)` method.
+ * Added the `isMetadataDocumentUsed()` method.
+ * Added the `setMetadataDocumentUsed(boolean)` method.
+
+- `BackchannelAuthenticationResponse` class
+ * Added the `getMetadataDocumentLocation()` method.
+ * Added the `setMetadataDocumentLocation(URI)` method.
+ * Added the `isMetadataDocumentUsed()` method.
+ * Added the `setMetadataDocumentUsed(boolean)` method.
+
+- `Client` class
+ * Added the `getMetadataDocumentLocation()` method.
+ * Added the `setMetadataDocumentLocation(URI)` method.
+ * Added the `getMetadataDocumentExpiresAt()` method.
+ * Added the `setMetadataDocumentExpiresAt(long)` method.
+ * Added the `getMetadataDocumentUpdatedAt()` method.
+ * Added the `setMetadataDocumentUpdatedAt(long)` method.
+ * Added the `isDiscoveredByMetadataDocument()` method.
+ * Added the `setDiscoveredByMetadataDocument(boolean)` method.
+ * Added the `getClientSource()` method.
+ * Added the `setClientSource(ClientSource)` method.
+ * Added the `updateClientSource()` method.
+
+- `ClientMetadataControl` class
+ * Added the `isMetadataDocumentLocationPreferred()` method.
+ * Added the `setMetadataDocumentLocationPreferred(boolean)` method.
+
+- `DeviceAuthorizationResponse` class
+ * Added the `getMetadataDocumentLocation()` method.
+ * Added the `setMetadataDocumentLocation(URI)` method.
+ * Added the `isMetadataDocumentUsed()` method.
+ * Added the `setMetadataDocumentUsed(boolean)` method.
+
+- `DeviceVerificationResponse` class
+ * Added the `getMetadataDocumentLocation()` method.
+ * Added the `setMetadataDocumentLocation(URI)` method.
+ * Added the `isMetadataDocumentUsed()` method.
+ * Added the `setMetadataDocumentUsed(boolean)` method.
+
+- `IntrospectionResponse` class
+ * Added the `getMetadataDocumentLocation()` method.
+ * Added the `setMetadataDocumentLocation(URI)` method.
+ * Added the `isMetadataDocumentUsed()` method.
+ * Added the `setMetadataDocumentUsed(boolean)` method.
+
+- `Service` class
+ * Added the `isClientIdMetadataDocumentSupported()` method.
+ * Added the `setClientIdMetadataDocumentSupported(boolean)` method.
+
+- `TokenCreateRequest` class
+ * Added the `isMetadataDocumentUsed()` method.
+ * Added the `setMetadataDocumentUsed(boolean)` method.
+
+- `TokenInfo` class
+ * Added the `getMetadataDocumentLocation()` method.
+ * Added the `setMetadataDocumentLocation(URI)` method.
+ * Added the `isMetadataDocumentUsed()` method.
+ * Added the `setMetadataDocumentUsed(boolean)` method.
+
+- `TokenIssueResponse` class
+ * Added the `getMetadataDocumentLocation()` method.
+ * Added the `setMetadataDocumentLocation(URI)` method.
+ * Added the `isMetadataDocumentUsed()` method.
+ * Added the `setMetadataDocumentUsed(boolean)` method.
+
+- `TokenResponse` class
+ * Added the `getMetadataDocumentLocation()` method.
+ * Added the `setMetadataDocumentLocation(URI)` method.
+ * Added the `isMetadataDocumentUsed()` method.
+ * Added the `setMetadataDocumentUsed(boolean)` method.
+
+- `UserInfoResponse` class
+ * Added the `getMetadataDocumentLocation()` method.
+ * Added the `setMetadataDocumentLocation(URI)` method.
+ * Added the `isMetadataDocumentUsed()` method.
+ * Added the `setMetadataDocumentUsed(boolean)` method.
+
+- New types
+ * `ClientSource` enum
+
+
4.28 (2025-11-05)
-----------------
diff --git a/src/main/java/com/authlete/common/dto/AuthorizationResponse.java b/src/main/java/com/authlete/common/dto/AuthorizationResponse.java
index 2b949d692..e2117a4ee 100644
--- a/src/main/java/com/authlete/common/dto/AuthorizationResponse.java
+++ b/src/main/java/com/authlete/common/dto/AuthorizationResponse.java
@@ -1021,7 +1021,7 @@
*/
public class AuthorizationResponse extends ApiResponse
{
- private static final long serialVersionUID = 21L;
+ private static final long serialVersionUID = 22L;
/**
@@ -1161,6 +1161,11 @@ public enum Action
*/
private boolean clientEntityIdUsed;
+ /**
+ * @since Authlete 3.0.22
+ */
+ private boolean metadataDocumentUsed;
+
/**
* @since Authlete 1.1
*/
@@ -1756,6 +1761,75 @@ public void setClientEntityIdUsed(boolean used)
}
+ /**
+ * Get 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.
+ *
+ *
+ * @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/BackchannelAuthenticationCompleteResponse.java b/src/main/java/com/authlete/common/dto/BackchannelAuthenticationCompleteResponse.java
index 67326120b..c4bb60e36 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.
+ *
+ *
+ * When {@link #isClientEntityIdUsed()} 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.
+ *
+ * Otherwise, if {@link #isMetadataDocumentUsed()} returns {@code true},
+ * this method returns the same value as
+ * {@link #getMetadataDocumentLocation()}{@code .toString()} does.
+ *
+ * In other cases, this method returns the string representation of
+ * the value returned from {@link #getClientId()}.
+ *
+ *
* @return
* The client identifier used in the backchannel authentication
* request.
@@ -607,6 +734,10 @@ else if (clientEntityIdUsed)
{
return clientEntityId.toString();
}
+ else if (metadataDocumentUsed)
+ {
+ return metadataDocumentLocation.toString();
+ }
else
{
return String.valueOf(clientId);
diff --git a/src/main/java/com/authlete/common/dto/BackchannelAuthenticationResponse.java b/src/main/java/com/authlete/common/dto/BackchannelAuthenticationResponse.java
index 73d6afb73..cc18e1c12 100644
--- a/src/main/java/com/authlete/common/dto/BackchannelAuthenticationResponse.java
+++ b/src/main/java/com/authlete/common/dto/BackchannelAuthenticationResponse.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. You may obtain a copy of
@@ -598,7 +598,7 @@
*/
public class BackchannelAuthenticationResponse extends ApiResponse
{
- private static final long serialVersionUID = 11L;
+ private static final long serialVersionUID = 12L;
/**
@@ -682,6 +682,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
*/
@@ -1087,18 +1103,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 {@link #isClientEntityIdUsed()} 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.
+ *
+ * Otherwise, if {@link #isMetadataDocumentUsed()} returns {@code true},
+ * this method returns the same value as
+ * {@link #getMetadataDocumentLocation()}{@code .toString()} does.
+ *
+ * In other cases, this method returns the string representation of
+ * the value returned from {@link #getClientId()}.
+ *
+ *
* @return
* The client identifier used in the backchannel authentication
* request.
@@ -1113,6 +1240,10 @@ else if (clientEntityIdUsed)
{
return clientEntityId.toString();
}
+ else if (metadataDocumentUsed)
+ {
+ return metadataDocumentLocation.toString();
+ }
else
{
return String.valueOf(clientId);
diff --git a/src/main/java/com/authlete/common/dto/Client.java b/src/main/java/com/authlete/common/dto/Client.java
index 418f9559c..9d6ec524d 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-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.
@@ -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,13 @@
*
* @see OpenID for Verifiable Credential Issuance
+ *
+ * @see OAuth Client ID Metadata Document
*/
public class Client implements Serializable
{
- private static final long serialVersionUID = 38L;
+ private static final long serialVersionUID = 39L;
/*
@@ -584,7 +588,7 @@ public class Client implements Serializable
private boolean mtlsEndpointAliasesUsed;
/**
- * Indicates if the client is inscope for token migration.
+ * Indicates if the client is in scope for token migration.
*
* @since Authlete 4.23
*/
@@ -645,6 +649,7 @@ public class Client implements Serializable
*/
private boolean explicitlyRegistered;
+
/*
* For Verifiable Credentials
*/
@@ -660,6 +665,52 @@ 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;
+
+
/**
* Get the client number.
*
@@ -5217,7 +5268,7 @@ public Client setMtlsEndpointAliasesUsed(boolean mtlsEndpointAliasesUsed)
/**
- * Get the value that indicates whether this Client is in scope for token migration.
+ * 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.
@@ -5249,6 +5300,360 @@ public Client setInScopeForTokenMigration(boolean inScopeForTokenMigration)
}
+ /**
+ * 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.
+ *
+ *
+ * {@link ClientSource#DYNAMIC_REGISTRATION DYNAMIC_REGISTRATION}
+ * if {@link #isDynamicallyRegistered()} returns {@code true}.
+ *
+ * {@link ClientSource#AUTOMATIC_REGISTRATION AUTOMATIC_REGISTRATION}
+ * if {@link #isAutomaticallyRegistered()} returns {@code true}.
+ *
+ * {@link ClientSource#EXPLICIT_REGISTRATION EXPLICIT_REGISTRATION}
+ * if {@link #isExplicitlyRegistered()} returns {@code true}.
+ *
+ * {@link ClientSource#METADATA_DOCUMENT METADATA_DOCUMENT}
+ * if {@link #isDiscoveredByMetadataDocument()} returns {@code true}.
+ *
+ * {@link ClientSource#STATIC_REGISTRATION STATIC_REGISTRATION}
+ * in other cases.
+ *
+ *
+ * @return
+ * {@code this} object.
+ *
+ * @since 4.29
+ */
+ public Client updateClientSource()
+ {
+ return updateClientSource(this);
+ }
+
+
+ private static Client updateClientSource(Client client)
+ {
+ if (client == null)
+ {
+ return null;
+ }
+
+ // Derive the client source from fields.
+ ClientSource cs = deriveClientSource(client);
+
+ return client.setClientSource(cs);
+ }
+
+
+ private static ClientSource deriveClientSource(Client client)
+ {
+ if (client == null)
+ {
+ return null;
+ }
+
+ if (client.isDynamicallyRegistered())
+ {
+ // RFC 7591: OAuth 2.0 Dynamic Client Registration Protocol, or
+ // OpenID Connect Dynamic Client Registration 1.0.
+ return ClientSource.DYNAMIC_REGISTRATION;
+ }
+ else if (client.isAutomaticallyRegistered())
+ {
+ // Automatic registration defined in OpenID Federation 1.0.
+ return ClientSource.AUTOMATIC_REGISTRATION;
+ }
+ else if (client.isExplicitlyRegistered())
+ {
+ // Explicit registration defined in OpenID Federation 1.0.
+ return ClientSource.EXPLICIT_REGISTRATION;
+ }
+ else if (client.isDiscoveredByMetadataDocument())
+ {
+ // Discovery using OAuth Client ID Metadata Document.
+ return ClientSource.METADATA_DOCUMENT;
+ }
+ else
+ {
+ // Static registration.
+ return ClientSource.STATIC_REGISTRATION;
+ }
+ }
+
+
/**
* Get a {@code Map} instance that represents a set of standard client
* metadata.
@@ -5568,6 +5973,12 @@ else if (control.isEntityIdPreferred() && getEntityId() != null)
// Use the entity ID as the value of "client_id".
value = getEntityId().toString();
}
+ // If "metadata document location" is preferred and available.
+ else if (control.isMetadataDocumentLocationPreferred() && getMetadataDocumentLocation() != null)
+ {
+ // Use the metadata document location as the value of "client_id".
+ value = getMetadataDocumentLocation().toString();
+ }
else
{
// Use the original numeric client ID as the value of "client_id".
diff --git a/src/main/java/com/authlete/common/dto/DeviceAuthorizationResponse.java b/src/main/java/com/authlete/common/dto/DeviceAuthorizationResponse.java
index 347db3401..423c0995f 100644
--- a/src/main/java/com/authlete/common/dto/DeviceAuthorizationResponse.java
+++ b/src/main/java/com/authlete/common/dto/DeviceAuthorizationResponse.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
@@ -170,7 +170,7 @@
*/
public class DeviceAuthorizationResponse extends ApiResponse
{
- private static final long serialVersionUID = 9L;
+ private static final long serialVersionUID = 10L;
/**
@@ -249,6 +249,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
*/
@@ -622,17 +638,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 {@link #isClientEntityIdUsed()} 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.
+ *
+ * Otherwise, if {@link #isMetadataDocumentUsed()} returns {@code true},
+ * this method returns the same value as
+ * {@link #getMetadataDocumentLocation()}{@code .toString()} does.
+ *
+ * In other cases, this method returns the string representation of
+ * the value returned from {@link #getClientId()}.
+ *
+ *
* @return
* The client identifier used in the device authorization request.
*/
@@ -646,6 +773,10 @@ else if (clientEntityIdUsed)
{
return clientEntityId.toString();
}
+ else if (metadataDocumentUsed)
+ {
+ return metadataDocumentLocation.toString();
+ }
else
{
return String.valueOf(clientId);
diff --git a/src/main/java/com/authlete/common/dto/DeviceVerificationResponse.java b/src/main/java/com/authlete/common/dto/DeviceVerificationResponse.java
index 5487dda57..c8023dbaa 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.
+ *
+ *
+ * When {@link #isClientEntityIdUsed()} 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.
+ *
+ * Otherwise, if {@link #isMetadataDocumentUsed()} returns {@code true},
+ * this method returns the same value as
+ * {@link #getMetadataDocumentLocation()}{@code .toString()} does.
+ *
+ * In other cases, this method returns the string representation of
+ * the value returned from {@link #getClientId()}.
+ *
+ *
* @return
* The client identifier used in the device authorization request
* for the user code.
@@ -479,6 +606,10 @@ else if (clientEntityIdUsed)
{
return clientEntityId.toString();
}
+ else if (metadataDocumentUsed)
+ {
+ return metadataDocumentLocation.toString();
+ }
else
{
return String.valueOf(clientId);
diff --git a/src/main/java/com/authlete/common/dto/IntrospectionResponse.java b/src/main/java/com/authlete/common/dto/IntrospectionResponse.java
index e19915141..fa19e6f42 100644
--- a/src/main/java/com/authlete/common/dto/IntrospectionResponse.java
+++ b/src/main/java/com/authlete/common/dto/IntrospectionResponse.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.
@@ -268,7 +268,7 @@
*/
public class IntrospectionResponse extends ApiResponse
{
- private static final long serialVersionUID = 23L;
+ private static final long serialVersionUID = 24L;
/**
@@ -442,6 +442,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;
+
+
/**
* Confirmation hash for MTLS-bound access tokens. Currently only the S256
* type is supported and is assumed.
@@ -1091,6 +1110,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 client certificate thumbprint used to validate the access token.
*
diff --git a/src/main/java/com/authlete/common/dto/Service.java b/src/main/java/com/authlete/common/dto/Service.java
index df13c50af..559ce733f 100644
--- a/src/main/java/com/authlete/common/dto/Service.java
+++ b/src/main/java/com/authlete/common/dto/Service.java
@@ -330,7 +330,7 @@
*/
public class Service implements Serializable
{
- private static final long serialVersionUID = 84L;
+ private static final long serialVersionUID = 85L;
/*
@@ -1893,6 +1893,17 @@ public class Service implements Serializable
private String oid4vciVersion;
+ /**
+ * Whether to support OAuth Client ID Metadata Document .
+ *
+ * @since 4.29
+ * @since Authlete 3.0.22
+ */
+ private boolean clientIdMetadataDocumentSupported;
+
+
/**
* Get the service number.
*
@@ -12135,4 +12146,59 @@ public Service setOid4vciVersion(String version)
return this;
}
+
+
+ /**
+ * Get the flag to determine to support OAuth Client ID Metadata Document .
+ *
+ *
+ * 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;
+ }
}
diff --git a/src/main/java/com/authlete/common/dto/TokenCreateRequest.java b/src/main/java/com/authlete/common/dto/TokenCreateRequest.java
index 8006269a3..d1afdca89 100644
--- a/src/main/java/com/authlete/common/dto/TokenCreateRequest.java
+++ b/src/main/java/com/authlete/common/dto/TokenCreateRequest.java
@@ -275,7 +275,7 @@
*/
public class TokenCreateRequest implements Serializable
{
- private static final long serialVersionUID = 15L;
+ private static final long serialVersionUID = 16L;
private GrantType grantType;
@@ -287,6 +287,7 @@ public class TokenCreateRequest implements Serializable
private Property[] properties;
private boolean clientIdAliasUsed;
private boolean clientEntityIdUsed;
+ private boolean metadataDocumentUsed;
private String accessToken;
private String refreshToken;
private boolean accessTokenPersistent;
@@ -595,8 +596,8 @@ public TokenCreateRequest setProperties(Property[] properties)
*
*
*
- * 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/TokenInfo.java b/src/main/java/com/authlete/common/dto/TokenInfo.java
index 20d0621f4..c087f8034 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 823f6376f..8a7423548 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/TokenResponse.java b/src/main/java/com/authlete/common/dto/TokenResponse.java
index 7b1a86c71..17bf78bb3 100644
--- a/src/main/java/com/authlete/common/dto/TokenResponse.java
+++ b/src/main/java/com/authlete/common/dto/TokenResponse.java
@@ -1187,7 +1187,7 @@
*/
public class TokenResponse extends ApiResponse
{
- private static final long serialVersionUID = 21L;
+ private static final long serialVersionUID = 22L;
/**
@@ -1414,6 +1414,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
*/
@@ -2212,6 +2228,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/UserInfoResponse.java b/src/main/java/com/authlete/common/dto/UserInfoResponse.java
index dc15d2efb..6820f70e1 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/types/ClientSource.java b/src/main/java/com/authlete/common/types/ClientSource.java
new file mode 100644
index 000000000..0db40b51a
--- /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/util/ClientMetadataControl.java b/src/main/java/com/authlete/common/util/ClientMetadataControl.java
index 5d62a51ef..0a76924dc 100644
--- a/src/main/java/com/authlete/common/util/ClientMetadataControl.java
+++ b/src/main/java/com/authlete/common/util/ClientMetadataControl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 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.
@@ -27,6 +27,7 @@ public class ClientMetadataControl extends MapControl
private boolean customIncluded;
private boolean aliasPreferred;
private boolean entityIdPreferred;
+ private boolean metadataDocumentLocationPreferred;
@Override
@@ -179,6 +180,9 @@ public ClientMetadataControl setAliasPreferred(boolean preferred)
* @return
* {@code true} if the entity ID is used as the value of the
* {@code client_id} property when available.
+ *
+ * @see
+ * OpenID Federation 1.0
*/
public boolean isEntityIdPreferred()
{
@@ -196,6 +200,9 @@ public boolean isEntityIdPreferred()
*
* @return
* {@code this} object.
+ *
+ * @see
+ * OpenID Federation 1.0
*/
public ClientMetadataControl setEntityIdPreferred(boolean preferred)
{
@@ -203,4 +210,53 @@ public ClientMetadataControl setEntityIdPreferred(boolean preferred)
return this;
}
+
+
+ /**
+ * Get the flag whether to use the location of the client's metadata
+ * document as the value of the {@code client_id} property when
+ * available.
+ *
+ * @return
+ * {@code true} if the location of the client's metadata
+ * document is used as the value of the {@code client_id}
+ * property when available.
+ *
+ * @since 4.29
+ * @since Authlete 3.0.22
+ *
+ * @see
+ * OAuth Client ID Metadata Document
+ */
+ public boolean isMetadataDocumentLocationPreferred()
+ {
+ return metadataDocumentLocationPreferred;
+ }
+
+
+ /**
+ * Set the flag whether to use the location of the client's metadata
+ * document as the value of the {@code client_id} property when
+ * available.
+ *
+ * @param preferred
+ * {@code true} to use the location of the client's metadata
+ * document as the value of the {@code client_id} property
+ * when available.
+ *
+ * @return
+ * {@code this} object.
+ *
+ * @since 4.29
+ * @since Authlete 3.0.22
+ *
+ * @see
+ * OAuth Client ID Metadata Document
+ */
+ public ClientMetadataControl setMetadataDocumentLocationPreferred(boolean preferred)
+ {
+ this.metadataDocumentLocationPreferred = preferred;
+
+ return this;
+ }
}
From 53714f5a4fced0e655233320fbf69eafdbc6f5ab Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Tue, 18 Nov 2025 19:31:49 +0900
Subject: [PATCH 044/132] Updated CHANGES files for version 4.29.
---
CHANGES.ja.md | 3 +++
CHANGES.md | 3 +++
2 files changed, 6 insertions(+)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index a47afeeba..abe50f638 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,9 @@
変更点
======
+4.29 (2025 年 11 月 18 日)
+--------------------------
+
- `AuthorizationResponse` クラス
* `isMetadataDocumentUsed()` メソッドを追加。
* `setMetadataDocumentUsed(boolean)` メソッドを追加。
diff --git a/CHANGES.md b/CHANGES.md
index 5d9235400..3bf351f69 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,9 @@
CHANGES
=======
+4.29 (2025-11-18)
+-----------------
+
- `AuthorizationResponse` class
* Added the `isMetadataDocumentUsed()` method.
* Added the `setMetadataDocumentUsed(boolean)` method.
From da710e4e73fee5fbc3d2dc33045b6adc0c09243f Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Tue, 18 Nov 2025 10:34:48 +0000
Subject: [PATCH 045/132] [maven-release-plugin] prepare release
authlete-java-common-4.29
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 0a468625a..2ef810713 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.29-SNAPSHOT
+ 4.29
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.21
+ authlete-java-common-4.29
From fc4011512040a7832a813a968586bd47afed24a1 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Tue, 18 Nov 2025 10:34:50 +0000
Subject: [PATCH 046/132] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 2ef810713..c7160926b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.29
+ 4.30-SNAPSHOT
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.29
+ authlete-java-common-4.21
From e03e983fdf4b755e49b954cb38948c38e8440d2d Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Thu, 20 Nov 2025 22:13:46 +0900
Subject: [PATCH 047/132] feat: CIMD Options (#149)
---
CHANGES.ja.md | 32 ++
CHANGES.md | 32 ++
.../common/dto/AuthorizationRequest.java | 58 +-
.../dto/BackchannelAuthenticationRequest.java | 58 +-
.../com/authlete/common/dto/CimdOptions.java | 300 +++++++++++
.../dto/DeviceAuthorizationRequest.java | 58 +-
.../java/com/authlete/common/dto/Service.java | 509 +++++++++++++++++-
.../com/authlete/common/dto/TokenRequest.java | 58 +-
8 files changed, 1096 insertions(+), 9 deletions(-)
create mode 100644 src/main/java/com/authlete/common/dto/CimdOptions.java
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index abe50f638..9ce0e460a 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,38 @@
変更点
======
+- `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 日)
--------------------------
diff --git a/CHANGES.md b/CHANGES.md
index 3bf351f69..8172feb20 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,38 @@
CHANGES
=======
+- `AuthorizationRequest` class
+ * Added the `getCimdOptions()` method.
+ * Added the `setCimdOptions(CimdOptions)` method.
+
+- `BackchannelAuthenticationRequest` class
+ * Added the `getCimdOptions()` method.
+ * Added the `setCimdOptions(CimdOptions)` method.
+
+- `DeviceAuthorizationRequest` class
+ * Added the `getCimdOptions()` method.
+ * Added the `setCimdOptions(CimdOptions)` method.
+
+- `Service` class
+ * Added the `isCimdWhitelistEnabled()` method.
+ * Added the `setCimdWhitelistEnabled(boolean)` method.
+ * Added the `getCimdWhitelist()` method.
+ * Added the `setCimdWhitelist(String[])` method.
+ * Added the `isCimdAlwaysRetrieved()` method.
+ * Added the `setCimdAlwaysRetrieved(boolean)` method.
+ * Added the `isCimdHttpPermitted()` method.
+ * Added the `setCimdHttpPermitted(boolean)` method.
+ * Added the `isCimdQueryPermitted()` method.
+ * Added the `setCimdQueryPermitted(boolean)` method.
+
+- `TokenRequest` class
+ * Added the `getCimdOptions()` method.
+ * Added the `setCimdOptions(CimdOptions)` method.
+
+- New types
+ * `CimdOptions` class
+
+
4.29 (2025-11-18)
-----------------
diff --git a/src/main/java/com/authlete/common/dto/AuthorizationRequest.java b/src/main/java/com/authlete/common/dto/AuthorizationRequest.java
index 5c2aeabd0..4a0d2f696 100644
--- a/src/main/java/com/authlete/common/dto/AuthorizationRequest.java
+++ b/src/main/java/com/authlete/common/dto/AuthorizationRequest.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.
@@ -50,7 +50,7 @@
*/
public class AuthorizationRequest implements Serializable
{
- private static final long serialVersionUID = 3L;
+ private static final long serialVersionUID = 4L;
/**
@@ -69,6 +69,15 @@ public class AuthorizationRequest implements Serializable
private String context;
+ /**
+ * 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 authorization endpoint of the
@@ -184,4 +193,49 @@ public AuthorizationRequest setContext(String context)
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 AuthorizationRequest setCimdOptions(CimdOptions options)
+ {
+ this.cimdOptions = options;
+
+ return this;
+ }
}
diff --git a/src/main/java/com/authlete/common/dto/BackchannelAuthenticationRequest.java b/src/main/java/com/authlete/common/dto/BackchannelAuthenticationRequest.java
index 68f0151cf..6aa3157c7 100644
--- a/src/main/java/com/authlete/common/dto/BackchannelAuthenticationRequest.java
+++ b/src/main/java/com/authlete/common/dto/BackchannelAuthenticationRequest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2024 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.
@@ -124,7 +124,7 @@
*/
public class BackchannelAuthenticationRequest implements Serializable
{
- private static final long serialVersionUID = 2L;
+ private static final long serialVersionUID = 3L;
/**
@@ -181,6 +181,15 @@ public class BackchannelAuthenticationRequest 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 backchannel authentication endpoint of the OpenID provider
@@ -446,4 +455,49 @@ public BackchannelAuthenticationRequest 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 BackchannelAuthenticationRequest setCimdOptions(CimdOptions options)
+ {
+ this.cimdOptions = options;
+
+ 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 000000000..f7501d02b
--- /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 a whitelist is used (see {@link
+ * Service#isCimdWhitelistEnabled()}).
+ *
+ *
+ *
+ * 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 a whitelist is used (see {@link
+ * Service#isCimdWhitelistEnabled()}).
+ *
+ *
+ *
+ * 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/DeviceAuthorizationRequest.java b/src/main/java/com/authlete/common/dto/DeviceAuthorizationRequest.java
index 236b44d12..1c1c8ebd6 100644
--- a/src/main/java/com/authlete/common/dto/DeviceAuthorizationRequest.java
+++ b/src/main/java/com/authlete/common/dto/DeviceAuthorizationRequest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019-2024 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.
@@ -124,7 +124,7 @@
*/
public class DeviceAuthorizationRequest implements Serializable
{
- private static final long serialVersionUID = 2L;
+ private static final long serialVersionUID = 3L;
/**
@@ -181,6 +181,15 @@ public class DeviceAuthorizationRequest 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 device authorization endpoint of the authorization server
@@ -446,4 +455,49 @@ public DeviceAuthorizationRequest 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 DeviceAuthorizationRequest setCimdOptions(CimdOptions options)
+ {
+ this.cimdOptions = options;
+
+ return this;
+ }
}
diff --git a/src/main/java/com/authlete/common/dto/Service.java b/src/main/java/com/authlete/common/dto/Service.java
index 559ce733f..f460d11fc 100644
--- a/src/main/java/com/authlete/common/dto/Service.java
+++ b/src/main/java/com/authlete/common/dto/Service.java
@@ -330,7 +330,7 @@
*/
public class Service implements Serializable
{
- private static final long serialVersionUID = 85L;
+ private static final long serialVersionUID = 86L;
/*
@@ -1904,6 +1904,53 @@ public class Service implements Serializable
private boolean clientIdMetadataDocumentSupported;
+ /**
+ * Whether to enable the whitelist for client IDs in the CIMD context.
+ *
+ * @since 4.30
+ * @since Authlete 3.0.22
+ */
+ private boolean cimdWhitelistEnabled;
+
+
+ /**
+ * The whitelist for client IDs in the CIMD context.
+ *
+ * @since 4.30
+ * @since Authlete 3.0.22
+ */
+ private String[] cimdWhitelist;
+
+
+ /**
+ * 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;
+
+
/**
* Get the service number.
*
@@ -12201,4 +12248,464 @@ public Service setClientIdMetadataDocumentSupported(boolean supported)
return this;
}
+
+
+ /**
+ * Get the flag that indicates whether the whitelist for client IDs
+ * in the CIMD context is enabled or not.
+ *
+ *
+ * If the whitelist is enabled, the client ID in a request must match at
+ * least one entry in the whitelist to be considered a valid client ID
+ * in the CIMD context.
+ *
+ *
+ * @return
+ * {@code true} if the whitelist for client IDs in the CIMD
+ * context is enabled.
+ *
+ * @since 4.30
+ * @since Authlete 3.0.22
+ *
+ * @see
+ * OAuth Client ID Metadata Document
+ */
+ public boolean isCimdWhitelistEnabled()
+ {
+ return cimdWhitelistEnabled;
+ }
+
+
+ /**
+ * Set the flag that indicates whether the whitelist for client IDs
+ * in the CIMD context is enabled or not.
+ *
+ *
+ * If the whitelist is enabled, the client ID in a request must match at
+ * least one entry in the whitelist to be considered a valid client ID
+ * in the CIMD context.
+ *
+ *
+ * @param enabled
+ * {@code true} to enable the whitelist for client IDs in the
+ * CIMD context.
+ *
+ * @return
+ * {@code this} object.
+ *
+ * @since 4.30
+ * @since Authlete 3.0.22
+ *
+ * @see
+ * OAuth Client ID Metadata Document
+ */
+ public Service setCimdWhitelistEnabled(boolean enabled)
+ {
+ this.cimdWhitelistEnabled = enabled;
+
+ return this;
+ }
+
+
+ /**
+ * Get the whitelist for client IDs in the CIMD context.
+ *
+ *
+ * When the whitelist feature is enabled (see {@link #isCimdWhitelistEnabled()}),
+ * the whitelist 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
+ * whitelist.
+ *
+ *
+ *
+ * Each entry in the whitelist must be a valid URI. The comparison between
+ * a whitelist entry and a client ID is performed as follows:
+ *
+ *
+ *
+ * [scheme] Simple string comparison.
+ * [authority] Simple string comparison.
+ * [path] The client ID's path must contain all path segments of
+ * the whitelist entry in the same order.
+ * [query] Simple string comparison, but only if the whitelist
+ * entry has a query component.
+ *
+ *
+ *
+ * For example, if the whitelist 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 whitelist for client IDs in the CIMD context.
+ *
+ * @since 4.30
+ * @since Authlete 3.0.22
+ *
+ * @see
+ * OAuth Client ID Metadata Document
+ */
+ public String[] getCimdWhitelist()
+ {
+ return cimdWhitelist;
+ }
+
+
+ /**
+ * Set the whitelist for client IDs in the CIMD context.
+ *
+ *
+ * When the whitelist feature is enabled (see {@link #isCimdWhitelistEnabled()}),
+ * the whitelist 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
+ * whitelist.
+ *
+ *
+ *
+ * Each entry in the whitelist must be a valid URI. The comparison between
+ * a whitelist entry and a client ID is performed as follows:
+ *
+ *
+ *
+ * [scheme] Simple string comparison.
+ * [authority] Simple string comparison.
+ * [path] The client ID's path must contain all path segments of
+ * the whitelist entry in the same order.
+ * [query] Simple string comparison, but only if the whitelist
+ * entry has a query component.
+ *
+ *
+ *
+ * For example, if the whitelist 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 whitelist
+ * The whitelist for client IDs in the CIMD context.
+ *
+ * @return
+ * {@code this} object.
+ *
+ * @since 4.30
+ * @since Authlete 3.0.22
+ *
+ * @see
+ * OAuth Client ID Metadata Document
+ */
+ public Service setCimdWhitelist(String[] whitelist)
+ {
+ this.cimdWhitelist = whitelist;
+
+ 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 a whitelist is used (see {@link
+ * Service#isCimdWhitelistEnabled()}).
+ *
+ *
+ *
+ * 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 a whitelist is used (see {@link
+ * Service#isCimdWhitelistEnabled()}).
+ *
+ *
+ *
+ * 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;
+ }
}
diff --git a/src/main/java/com/authlete/common/dto/TokenRequest.java b/src/main/java/com/authlete/common/dto/TokenRequest.java
index f0be40df2..46a68f24c 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;
+ }
}
From 24eea002db2a198bc6812579ade5d6bbea2cd2b5 Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Thu, 20 Nov 2025 22:15:58 +0900
Subject: [PATCH 048/132] Updated CHANGES files for version 4.30.
---
CHANGES.ja.md | 3 +++
CHANGES.md | 3 +++
2 files changed, 6 insertions(+)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index 9ce0e460a..f6d751003 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,9 @@
変更点
======
+4.30 (2025 年 11 月 20 日)
+--------------------------
+
- `AuthorizationRequest` クラス
* `getCimdOptions()` メソッドを追加。
* `setCimdOptions(CimdOptions)` メソッドを追加。
diff --git a/CHANGES.md b/CHANGES.md
index 8172feb20..f00b6c514 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,9 @@
CHANGES
=======
+4.30 (2025-11-20)
+-----------------
+
- `AuthorizationRequest` class
* Added the `getCimdOptions()` method.
* Added the `setCimdOptions(CimdOptions)` method.
From 0da6dc8083097a7c9848a2e375e4f7bf80d0b028 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Thu, 20 Nov 2025 13:18:32 +0000
Subject: [PATCH 049/132] [maven-release-plugin] prepare release
authlete-java-common-4.30
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index c7160926b..53dfea1dc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.30-SNAPSHOT
+ 4.30
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.21
+ authlete-java-common-4.30
From b81d569f5e5b6864c93f98930795109c2feb3e4b Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Thu, 20 Nov 2025 13:18:34 +0000
Subject: [PATCH 050/132] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 53dfea1dc..886e95cf2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.30
+ 4.31-SNAPSHOT
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.30
+ authlete-java-common-4.21
From 8c8954b51781d3939bda7ec18a2d00952df53379 Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Tue, 25 Nov 2025 14:31:16 +0900
Subject: [PATCH 051/132] misc: Rename Whitelist to Allowlist (#150)
---
CHANGES.ja.md | 11 ++
CHANGES.md | 11 ++
.../com/authlete/common/dto/CimdOptions.java | 8 +-
.../java/com/authlete/common/dto/Service.java | 104 +++++++++---------
4 files changed, 78 insertions(+), 56 deletions(-)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index f6d751003..d159bfa84 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,17 @@
変更点
======
+- `Service` クラス
+ * `isCimdAllowlistEnabled()` メソッドを追加。
+ * `setCimdAllowlistEnabled(boolean)` メソッドを追加。
+ * `getCimdAllowlist()` メソッドを追加。
+ * `setCimdAllowlist(String[])` メソッドを追加。
+ * `isCimdWhitelistEnabled()` メソッドを削除。
+ * `setCimdWhitelistEnabled(boolean)` メソッドを削除。
+ * `getCimdWhitelist()` メソッドを削除。
+ * `setCimdWhitelist(String[])` メソッドを削除。
+
+
4.30 (2025 年 11 月 20 日)
--------------------------
diff --git a/CHANGES.md b/CHANGES.md
index f00b6c514..0fa868963 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,17 @@
CHANGES
=======
+- `Service` class
+ * Added the `isCimdAllowlistEnabled()` method.
+ * Added the `setCimdAllowlistEnabled(boolean)` method.
+ * Added the `getCimdAllowlist()` method.
+ * Added the `setCimdAllowlist(String[])` method.
+ * Removed the `isCimdWhitelistEnabled()` method.
+ * Removed the `setCimdWhitelistEnabled(boolean)` method.
+ * Removed the `getCimdWhitelist()` method.
+ * Removed the `setCimdWhitelist(String[])` method.
+
+
4.30 (2025-11-20)
-----------------
diff --git a/src/main/java/com/authlete/common/dto/CimdOptions.java b/src/main/java/com/authlete/common/dto/CimdOptions.java
index f7501d02b..a02389ed3 100644
--- a/src/main/java/com/authlete/common/dto/CimdOptions.java
+++ b/src/main/java/com/authlete/common/dto/CimdOptions.java
@@ -182,8 +182,8 @@ public CimdOptions setAlwaysRetrieved(boolean always)
*
*
* Given this purpose, it is not recommended to enable this option in
- * production environments unless a whitelist is used (see {@link
- * Service#isCimdWhitelistEnabled()}).
+ * production environments unless an allowlist is used (see {@link
+ * Service#isCimdAllowlistEnabled()}).
*
*
*
@@ -216,8 +216,8 @@ public boolean isHttpPermitted()
*
*
* Given this purpose, it is not recommended to enable this option in
- * production environments unless a whitelist is used (see {@link
- * Service#isCimdWhitelistEnabled()}).
+ * production environments unless an allowlist is used (see {@link
+ * Service#isCimdAllowlistEnabled()}).
*
*
*
diff --git a/src/main/java/com/authlete/common/dto/Service.java b/src/main/java/com/authlete/common/dto/Service.java
index f460d11fc..8eaf45a32 100644
--- a/src/main/java/com/authlete/common/dto/Service.java
+++ b/src/main/java/com/authlete/common/dto/Service.java
@@ -330,7 +330,7 @@
*/
public class Service implements Serializable
{
- private static final long serialVersionUID = 86L;
+ private static final long serialVersionUID = 87L;
/*
@@ -1905,21 +1905,21 @@ public class Service implements Serializable
/**
- * Whether to enable the whitelist for client IDs in the CIMD context.
+ * Whether to enable the allowlist for client IDs in the CIMD context.
*
- * @since 4.30
+ * @since 4.31
* @since Authlete 3.0.22
*/
- private boolean cimdWhitelistEnabled;
+ private boolean cimdAllowlistEnabled;
/**
- * The whitelist for client IDs in the CIMD context.
+ * The allowlist for client IDs in the CIMD context.
*
- * @since 4.30
+ * @since 4.31
* @since Authlete 3.0.22
*/
- private String[] cimdWhitelist;
+ private String[] cimdAllowlist;
/**
@@ -12251,162 +12251,162 @@ public Service setClientIdMetadataDocumentSupported(boolean supported)
/**
- * Get the flag that indicates whether the whitelist for client IDs
+ * Get the flag that indicates whether the allowlist for client IDs
* in the CIMD context is enabled or not.
*
*
- * If the whitelist is enabled, the client ID in a request must match at
- * least one entry in the whitelist to be considered a valid client ID
+ * 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 whitelist for client IDs in the CIMD
+ * {@code true} if the allowlist for client IDs in the CIMD
* context is enabled.
*
- * @since 4.30
+ * @since 4.31
* @since Authlete 3.0.22
*
* @see
* OAuth Client ID Metadata Document
*/
- public boolean isCimdWhitelistEnabled()
+ public boolean isCimdAllowlistEnabled()
{
- return cimdWhitelistEnabled;
+ return cimdAllowlistEnabled;
}
/**
- * Set the flag that indicates whether the whitelist for client IDs
+ * Set the flag that indicates whether the allowlist for client IDs
* in the CIMD context is enabled or not.
*
*
- * If the whitelist is enabled, the client ID in a request must match at
- * least one entry in the whitelist to be considered a valid client ID
+ * 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 whitelist for client IDs in the
+ * {@code true} to enable the allowlist for client IDs in the
* CIMD context.
*
* @return
* {@code this} object.
*
- * @since 4.30
+ * @since 4.31
* @since Authlete 3.0.22
*
* @see
* OAuth Client ID Metadata Document
*/
- public Service setCimdWhitelistEnabled(boolean enabled)
+ public Service setCimdAllowlistEnabled(boolean enabled)
{
- this.cimdWhitelistEnabled = enabled;
+ this.cimdAllowlistEnabled = enabled;
return this;
}
/**
- * Get the whitelist for client IDs in the CIMD context.
*
*
- * When the whitelist feature is enabled (see {@link #isCimdWhitelistEnabled()}),
- * the whitelist specified by this property is consulted to determine
+ * 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
- * whitelist.
+ * allowlist.
*
*
*
- * Each entry in the whitelist must be a valid URI. The comparison between
- * a whitelist entry and a client ID is performed as follows:
+ * Each entry in the allowlist must be a valid URI. The comparison between
+ * an allowlist entry and a client ID is performed as follows:
*
*
*
* [scheme] Simple string comparison.
* [authority] Simple string comparison.
* [path] The client ID's path must contain all path segments of
- * the whitelist entry in the same order.
- * [query] Simple string comparison, but only if the whitelist
+ * the allowlist entry in the same order.
+ * [query] Simple string comparison, but only if the allowlist
* entry has a query component.
*
*
*
- * For example, if the whitelist contains "{@code https://example.com/a/b}",
+ * 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 whitelist for client IDs in the CIMD context.
+ * The allowlist for client IDs in the CIMD context.
*
- * @since 4.30
+ * @since 4.31
* @since Authlete 3.0.22
*
* @see
* OAuth Client ID Metadata Document
*/
- public String[] getCimdWhitelist()
+ public String[] getCimdAllowlist()
{
- return cimdWhitelist;
+ return cimdAllowlist;
}
/**
- * Set the whitelist for client IDs in the CIMD context.
*
*
- * When the whitelist feature is enabled (see {@link #isCimdWhitelistEnabled()}),
- * the whitelist specified by this property is consulted to determine
+ * 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
- * whitelist.
+ * allowlist.
*
*
*
- * Each entry in the whitelist must be a valid URI. The comparison between
- * a whitelist entry and a client ID is performed as follows:
+ * Each entry in the allowlist must be a valid URI. The comparison between
+ * an allowlist entry and a client ID is performed as follows:
*
*
*
* [scheme] Simple string comparison.
* [authority] Simple string comparison.
* [path] The client ID's path must contain all path segments of
- * the whitelist entry in the same order.
- * [query] Simple string comparison, but only if the whitelist
+ * the allowlist entry in the same order.
+ * [query] Simple string comparison, but only if the allowlist
* entry has a query component.
*
*
*
- * For example, if the whitelist contains "{@code https://example.com/a/b}",
+ * 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 whitelist
- * The whitelist for client IDs in the CIMD context.
+ * @param allowlist
+ * The allowlist for client IDs in the CIMD context.
*
* @return
* {@code this} object.
*
- * @since 4.30
+ * @since 4.31
* @since Authlete 3.0.22
*
* @see
* OAuth Client ID Metadata Document
*/
- public Service setCimdWhitelist(String[] whitelist)
+ public Service setCimdAllowlist(String[] allowlist)
{
- this.cimdWhitelist = whitelist;
+ this.cimdAllowlist = allowlist;
return this;
}
@@ -12559,8 +12559,8 @@ public Service setCimdAlwaysRetrieved(boolean always)
*
*
* Given this purpose, it is not recommended to enable this option in
- * production environments unless a whitelist is used (see {@link
- * Service#isCimdWhitelistEnabled()}).
+ * production environments unless an allowlist is used (see {@link
+ * Service#isCimdAllowlistEnabled()}).
*
*
*
@@ -12602,8 +12602,8 @@ public boolean isCimdHttpPermitted()
*
*
* Given this purpose, it is not recommended to enable this option in
- * production environments unless a whitelist is used (see {@link
- * Service#isCimdWhitelistEnabled()}).
+ * production environments unless an allowlist is used (see {@link
+ * Service#isCimdAllowlistEnabled()}).
*
*
*
From a9c0a749dc879e351bd0d83fd389a291b4b2b739 Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Tue, 25 Nov 2025 14:32:25 +0900
Subject: [PATCH 052/132] Updated CHANGES files for version 4.31.
---
CHANGES.ja.md | 3 +++
CHANGES.md | 3 +++
2 files changed, 6 insertions(+)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index d159bfa84..dce0ef76f 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,9 @@
変更点
======
+4.31 (2025 年 11 月 25 日)
+--------------------------
+
- `Service` クラス
* `isCimdAllowlistEnabled()` メソッドを追加。
* `setCimdAllowlistEnabled(boolean)` メソッドを追加。
diff --git a/CHANGES.md b/CHANGES.md
index 0fa868963..82916a0de 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,9 @@
CHANGES
=======
+4.31 (2025-11-25)
+-----------------
+
- `Service` class
* Added the `isCimdAllowlistEnabled()` method.
* Added the `setCimdAllowlistEnabled(boolean)` method.
From 2f55aae5f71219b1ab75e21390bd2d04cd37cb46 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Tue, 25 Nov 2025 05:35:33 +0000
Subject: [PATCH 053/132] [maven-release-plugin] prepare release
authlete-java-common-4.31
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 886e95cf2..756ed5d3b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.31-SNAPSHOT
+ 4.31
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.21
+ authlete-java-common-4.31
From 8a3c9135a2e0d79b4550b5a0e571297de644360b Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Tue, 25 Nov 2025 05:35:35 +0000
Subject: [PATCH 054/132] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 756ed5d3b..3ec104c0f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.31
+ 4.32-SNAPSHOT
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.31
+ authlete-java-common-4.21
From 7f4e46b2a464777604e4647290652e0f28fced09 Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Mon, 1 Dec 2025 14:59:09 +0900
Subject: [PATCH 055/132] feat: HTTP Alias Prohibited (#151)
---
CHANGES.ja.md | 5 +
CHANGES.md | 5 +
.../java/com/authlete/common/dto/Service.java | 95 ++++++++++++++++++-
3 files changed, 104 insertions(+), 1 deletion(-)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index dce0ef76f..4357347e9 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,11 @@
変更点
======
+- `Service` クラス
+ * `isHttpAliasProhibited()` メソッドを追加。
+ * `setHttpAliasProhibited(boolean)` メソッドを追加。
+
+
4.31 (2025 年 11 月 25 日)
--------------------------
diff --git a/CHANGES.md b/CHANGES.md
index 82916a0de..f3217c80e 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,11 @@
CHANGES
=======
+- `Service` class
+ * Added the `isHttpAliasProhibited()` method.
+ * Added the `setHttpAliasProhibited(boolean)` method.
+
+
4.31 (2025-11-25)
-----------------
diff --git a/src/main/java/com/authlete/common/dto/Service.java b/src/main/java/com/authlete/common/dto/Service.java
index 8eaf45a32..2af8139ba 100644
--- a/src/main/java/com/authlete/common/dto/Service.java
+++ b/src/main/java/com/authlete/common/dto/Service.java
@@ -330,7 +330,7 @@
*/
public class Service implements Serializable
{
- private static final long serialVersionUID = 87L;
+ private static final long serialVersionUID = 88L;
/*
@@ -1951,6 +1951,16 @@ public class Service implements Serializable
private boolean cimdQueryPermitted;
+ /**
+ * 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;
+
+
/**
* Get the service number.
*
@@ -4302,6 +4312,8 @@ public Service setErrorUriOmitted(boolean omitted)
* {@code false} if the feature is disabled.
*
* @since 2.2
+ *
+ * @see #isHttpAliasProhibited()
*/
public boolean isClientIdAliasEnabled()
{
@@ -4328,6 +4340,8 @@ public boolean isClientIdAliasEnabled()
* {@code this} object.
*
* @since 2.2
+ *
+ * @see #isHttpAliasProhibited()
*/
public Service setClientIdAliasEnabled(boolean enabled)
{
@@ -12708,4 +12722,83 @@ public Service setCimdQueryPermitted(boolean permitted)
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;
+ }
}
From ae3e18ea2c65afb67c8e29cadb2b53c220688a64 Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Mon, 1 Dec 2025 15:00:07 +0900
Subject: [PATCH 056/132] Updated CHANGES files for version 4.32.
---
CHANGES.ja.md | 3 +++
CHANGES.md | 3 +++
2 files changed, 6 insertions(+)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index 4357347e9..8dfd7dcfc 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,9 @@
変更点
======
+4.32 (2025 年 12 月 01 日)
+--------------------------
+
- `Service` クラス
* `isHttpAliasProhibited()` メソッドを追加。
* `setHttpAliasProhibited(boolean)` メソッドを追加。
diff --git a/CHANGES.md b/CHANGES.md
index f3217c80e..993327dab 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,9 @@
CHANGES
=======
+4.32 (2025-12-01)
+-----------------
+
- `Service` class
* Added the `isHttpAliasProhibited()` method.
* Added the `setHttpAliasProhibited(boolean)` method.
From f2fe12c9e7ed2916152194c50e213187f8089c53 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Mon, 1 Dec 2025 06:02:58 +0000
Subject: [PATCH 057/132] [maven-release-plugin] prepare release
authlete-java-common-4.32
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 3ec104c0f..552755485 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.32-SNAPSHOT
+ 4.32
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.21
+ authlete-java-common-4.32
From 6e997989d996546b29c2a4d78c89367dc5cd3c06 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Mon, 1 Dec 2025 06:03:00 +0000
Subject: [PATCH 058/132] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 552755485..24682b236 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.32
+ 4.33-SNAPSHOT
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.32
+ authlete-java-common-4.21
From 9ee8b1770b8d402315af9def85957e6364b32356 Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Sat, 13 Dec 2025 18:05:39 +0900
Subject: [PATCH 059/132] feat: CIMD Metadata Policy (#153)
---
CHANGES.ja.md | 7 +
CHANGES.md | 7 +
.../java/com/authlete/common/dto/Service.java | 222 +++++++++++++++++-
3 files changed, 235 insertions(+), 1 deletion(-)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index 8dfd7dcfc..365798897 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,13 @@
変更点
======
+- `Service` クラス
+ * `isCimdMetadataPolicyEnabled()` メソッドを追加。
+ * `setCimdMetadataPolicyEnabled(boolean)` メソッドを追加。
+ * `getCimdMetadataPolicy()` メソッドを追加。
+ * `setCimdMetadataPolicy(String)` メソッドを追加。
+
+
4.32 (2025 年 12 月 01 日)
--------------------------
diff --git a/CHANGES.md b/CHANGES.md
index 993327dab..82d478492 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,13 @@
CHANGES
=======
+- `Service` class
+ * Added the `isCimdMetadataPolicyEnabled()` method.
+ * Added the `setCimdMetadataPolicyEnabled(boolean)` method.
+ * Added the `getCimdMetadataPolicy()` method.
+ * Added the `setCimdMetadataPolicy(String)` method.
+
+
4.32 (2025-12-01)
-----------------
diff --git a/src/main/java/com/authlete/common/dto/Service.java b/src/main/java/com/authlete/common/dto/Service.java
index 2af8139ba..9bd957527 100644
--- a/src/main/java/com/authlete/common/dto/Service.java
+++ b/src/main/java/com/authlete/common/dto/Service.java
@@ -330,7 +330,7 @@
*/
public class Service implements Serializable
{
- private static final long serialVersionUID = 88L;
+ private static final long serialVersionUID = 89L;
/*
@@ -1951,6 +1951,26 @@ public class Service implements Serializable
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://}.
@@ -12724,6 +12744,206 @@ public Service setCimdQueryPermitted(boolean permitted)
}
+ /**
+ * 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://}.
From c6b55bbf57c09c40a5dd624bf4f8f1f8e515a665 Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Sat, 13 Dec 2025 18:07:02 +0900
Subject: [PATCH 060/132] Update CHANGES files for version 4.33
---
CHANGES.ja.md | 3 +++
CHANGES.md | 3 +++
2 files changed, 6 insertions(+)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index 365798897..531633f27 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,9 @@
変更点
======
+4.33 (2025 年 12 月 13 日)
+--------------------------
+
- `Service` クラス
* `isCimdMetadataPolicyEnabled()` メソッドを追加。
* `setCimdMetadataPolicyEnabled(boolean)` メソッドを追加。
diff --git a/CHANGES.md b/CHANGES.md
index 82d478492..2dec84778 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,9 @@
CHANGES
=======
+4.33 (2025-12-13)
+-----------------
+
- `Service` class
* Added the `isCimdMetadataPolicyEnabled()` method.
* Added the `setCimdMetadataPolicyEnabled(boolean)` method.
From 5ddb215c676c02e456f57683301de767294631f5 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Sat, 13 Dec 2025 09:10:02 +0000
Subject: [PATCH 061/132] [maven-release-plugin] prepare release
authlete-java-common-4.33
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 24682b236..dfdf58b8d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.33-SNAPSHOT
+ 4.33
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.21
+ authlete-java-common-4.33
From 6a01d3bd2806d1523ea7b7ebd5b78b2691dce1da Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Sat, 13 Dec 2025 09:10:04 +0000
Subject: [PATCH 062/132] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index dfdf58b8d..4b8ea3997 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.33
+ 4.34-SNAPSHOT
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.33
+ authlete-java-common-4.21
From b08abb8294cabbe452b5579da88c8b21ab2b4d38 Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Sat, 3 Jan 2026 15:40:29 +0900
Subject: [PATCH 063/132] Add a copy constructor to AuthzDetailsElement
---
.../common/dto/AuthzDetailsElement.java | 41 ++++++++++++++++++-
1 file changed, 39 insertions(+), 2 deletions(-)
diff --git a/src/main/java/com/authlete/common/dto/AuthzDetailsElement.java b/src/main/java/com/authlete/common/dto/AuthzDetailsElement.java
index be8b29d4d..f69bf8ab8 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.
*
From 414fb35a56e04bf9f5e79684aced60be31743395 Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Sat, 3 Jan 2026 15:45:15 +0900
Subject: [PATCH 064/132] Add credentialConfigurationId and
credentialIdentifier properties to CredentialRequestInfo
---
.../common/dto/CredentialRequestInfo.java | 159 +++++++++++++++++-
1 file changed, 153 insertions(+), 6 deletions(-)
diff --git a/src/main/java/com/authlete/common/dto/CredentialRequestInfo.java b/src/main/java/com/authlete/common/dto/CredentialRequestInfo.java
index 97644f91b..eb9fc9582 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.
@@ -299,4 +336,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;
+ }
}
From 42fb203bff8e963727c7669f01f5f9f15d80b169 Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Sat, 3 Jan 2026 15:47:57 +0900
Subject: [PATCH 065/132] Updated CHANGES files for version 4.34.
---
CHANGES.ja.md | 14 ++++++++++++++
CHANGES.md | 14 ++++++++++++++
2 files changed, 28 insertions(+)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index 531633f27..c6c0f405d 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,20 @@
変更点
======
+4.34 (2026 年 01 月 03 日)
+--------------------------
+
+- `AuthzDetailsElement` クラス
+ * デフォルトコンストラクタを明示的に追加。
+ * コピーコンストラクタ (`AuthzDetailsElement(AuthzDetailsElement)`) を追加。
+
+- `CredentialRequestInfo` クラス
+ * `getCredentialConfigurationId()` メソッドを追加。
+ * `setCredentialConfigurationId(String)` メソッドを追加。
+ * `getCredentialIdentifier()` メソッドを追加。
+ * `setCredentialIdentifier(String)` メソッドを追加。
+
+
4.33 (2025 年 12 月 13 日)
--------------------------
diff --git a/CHANGES.md b/CHANGES.md
index 2dec84778..fda42bf12 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,20 @@
CHANGES
=======
+4.34 (2026-01-03)
+-----------------
+
+- `AuthzDetailsElement` class
+ * Added the default constructor explicitly.
+ * Added a copy constructor (`AuthzDetailsElement(AuthzDetailsElement)`).
+
+- `CredentialRequestInfo` class
+ * Added the `getCredentialConfigurationId()` method.
+ * Added the `setCredentialConfigurationId(String)` method.
+ * Added the `getCredentialIdentifier()` method.
+ * Added the `setCredentialIdentifier(String)` method.
+
+
4.33 (2025-12-13)
-----------------
From 9838e01cbe63fbf40978f1fbe649bff414421573 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Sat, 3 Jan 2026 06:50:32 +0000
Subject: [PATCH 066/132] [maven-release-plugin] prepare release
authlete-java-common-4.34
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 4b8ea3997..d7555f0dc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.34-SNAPSHOT
+ 4.34
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.21
+ authlete-java-common-4.34
From 1c71a24b93d8550e791a9e34b6023de81b96fc75 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Sat, 3 Jan 2026 06:50:34 +0000
Subject: [PATCH 067/132] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index d7555f0dc..3fb1ac042 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.34
+ 4.35-SNAPSHOT
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.34
+ authlete-java-common-4.21
From f28e81b71234b2d9969f208f8444978183282ef1 Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Sat, 10 Jan 2026 14:28:30 +0900
Subject: [PATCH 068/132] feat: OID4VCI 1.0 Final / parameters for (deferred)
credential responses (#154)
---
CHANGES.ja.md | 17 ++
CHANGES.md | 17 ++
.../dto/CredentialDeferredIssueRequest.java | 264 +++++++++++++++++-
.../dto/CredentialDeferredIssueResponse.java | 79 +++++-
.../dto/CredentialDeferredParseResponse.java | 13 +-
.../dto/CredentialSingleIssueRequest.java | 184 +++++++++++-
.../dto/CredentialSingleIssueResponse.java | 9 +-
7 files changed, 559 insertions(+), 24 deletions(-)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index c6c0f405d..fadd1fa75 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,23 @@
変更点
======
+- `CredentialDeferredIssueRequest` クラス
+ * `isDenied()` メソッドを追加。
+ * `setDenied(boolean)` メソッドを追加。
+ * `getInterval()` メソッドを追加。
+ * `setInterval(int)` メソッドを追加。
+
+- `CredentialDeferredIssueResponse.Action` 列挙型
+ * `ACCEPTED` を追加。
+ * `ACCEPTED_JWT` を追加。
+
+- `CredentialSingleIssueRequest` クラス
+ * `isDenied()` メソッドを追加。
+ * `setDenied(boolean)` メソッドを追加。
+ * `getInterval()` メソッドを追加。
+ * `setInterval(int)` メソッドを追加。
+
+
4.34 (2026 年 01 月 03 日)
--------------------------
diff --git a/CHANGES.md b/CHANGES.md
index fda42bf12..0df2b86e1 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,23 @@
CHANGES
=======
+- `CredentialDeferredIssueRequest` class
+ * Added the `isDenied()` method.
+ * Added the `setDenied(boolean)` method.
+ * Added the `getInterval()` method.
+ * Added the `setInterval(int)` method.
+
+- `CredentialDeferredIssueResponse.Action` enum
+ * Added `ACCEPTED`.
+ * Added `ACCEPTED_JWT`.
+
+- `CredentialSingleIssueRequest` class
+ * Added the `isDenied()` method.
+ * Added the `setDenied(boolean)` method.
+ * Added the `getInterval()` method.
+ * Added the `setInterval(int)` method.
+
+
4.34 (2026-01-03)
-----------------
diff --git a/src/main/java/com/authlete/common/dto/CredentialDeferredIssueRequest.java b/src/main/java/com/authlete/common/dto/CredentialDeferredIssueRequest.java
index 586316c0d..a8f419fa3 100644
--- a/src/main/java/com/authlete/common/dto/CredentialDeferredIssueRequest.java
+++ b/src/main/java/com/authlete/common/dto/CredentialDeferredIssueRequest.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
* deferred credential endpoint . The endpoint is defined in the "OpenID for Verifiable Credential Issuance " (OID4VCI) specification.
+ * >OpenID for Verifiable Credential Issuance 1.0" (OID4VCI) specification.
*
*
*
@@ -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:
*
*
+ *
+ * To issue a credential
+ * To deny the deferred credential request
+ * To notify that the requested credential is not ready yet
+ *
+ *
+ * Issuing a credential
+ *
*
- * 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}.
+ *
+ *
+ * Denying a deferred credential request
+ *
+ *
+ * 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".
+ *
+ *
+ * Notifying that the credential is not ready yet
+ *
+ *
+ * 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:
+ *
+ *
+ *
+ *
+ * Prepare an error response with the {@code issuance_pending} error code for
+ * OID4VCI 1.0 ID1.
+ *
+ * Prepare a successful response containing the {@code transaction_id} and
+ * {@code interval} parameters for OID4VCI 1.0 Final or later.
+ *
+ *
+ *
+ * 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 @@
* }
*
*
+ * Interval handling
+ *
+ *
+ * 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".
+ *
+ *
+ * Important note
+ *
+ *
+ * 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 the {@code order.issuanceDeferred} request parameter is
+ * {@code true}.
+ *
+ *
+ *
+ * The version of the OID4VCI specification is 1.0-Final or later (that is,
+ * the {@code oid4vciVersion} property of {@link Service} is set and its
+ * value is not {@code "1.0-ID1"}).
+ *
+ *
+ *
+ * @return
+ * 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
+ */
+ public int getInterval()
+ {
+ return interval;
+ }
+
+
+ /**
+ * Set 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 the {@code order.issuanceDeferred} request parameter is
+ * {@code true}.
+ *
+ *
+ *
+ * The version of the OID4VCI specification is 1.0-Final or later (that is,
+ * the {@code oid4vciVersion} property of {@link Service} is set and its
+ * value is not {@code "1.0-ID1"}).
+ *
+ *
+ *
+ * @param interval
+ * 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.
+ *
+ * @return
+ * {@code this} object.
+ *
+ * @since 4.35
+ * @since Authlete 3.0.25
+ */
+ public CredentialDeferredIssueRequest setInterval(int interval)
+ {
+ this.interval = interval;
+
+ return this;
+ }
}
diff --git a/src/main/java/com/authlete/common/dto/CredentialDeferredIssueResponse.java b/src/main/java/com/authlete/common/dto/CredentialDeferredIssueResponse.java
index e22a1efb3..312128516 100644
--- a/src/main/java/com/authlete/common/dto/CredentialDeferredIssueResponse.java
+++ b/src/main/java/com/authlete/common/dto/CredentialDeferredIssueResponse.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
@@ -71,6 +71,51 @@
*
*
*
+ * {@code action} = {@link Action#ACCEPTED ACCEPTED}
+ *
+ *
+ * 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.)
+ *
+ *
+ *
+ * {@code action} = {@link Action#ACCEPTED_JWT ACCEPTED_JWT}
+ *
+ *
+ * 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.)
+ *
+ *
+ *
* {@code action} = {@link Action#BAD_REQUEST BAD_REQUEST}
*
*
@@ -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 778bfda18..389148273 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/CredentialSingleIssueRequest.java b/src/main/java/com/authlete/common/dto/CredentialSingleIssueRequest.java
index a1a36d61a..e116ca52c 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 the {@code order.issuanceDeferred} request parameter is
+ * {@code true}.
+ *
+ *
+ *
+ * The version of the OID4VCI specification is 1.0-Final or later (that is,
+ * the {@code oid4vciVersion} property of {@link Service} is set and its
+ * value is not {@code "1.0-ID1"}).
+ *
+ *
+ *
+ * @return
+ * 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
+ */
+ public int getInterval()
+ {
+ return interval;
+ }
+
+
+ /**
+ * Set 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 the {@code order.issuanceDeferred} request parameter is
+ * {@code true}.
+ *
+ *
+ *
+ * The version of the OID4VCI specification is 1.0-Final or later (that is,
+ * the {@code oid4vciVersion} property of {@link Service} is set and its
+ * value is not {@code "1.0-ID1"}).
+ *
+ *
+ *
+ * @param interval
+ * 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.
+ *
+ * @return
+ * {@code this} object.
+ *
+ * @since 4.35
+ * @since Authlete 3.0.25
+ */
+ public CredentialSingleIssueRequest setInterval(int interval)
+ {
+ this.interval = interval;
+
+ return this;
+ }
}
diff --git a/src/main/java/com/authlete/common/dto/CredentialSingleIssueResponse.java b/src/main/java/com/authlete/common/dto/CredentialSingleIssueResponse.java
index c91f0f67d..64940b10c 100644
--- a/src/main/java/com/authlete/common/dto/CredentialSingleIssueResponse.java
+++ b/src/main/java/com/authlete/common/dto/CredentialSingleIssueResponse.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
@@ -292,6 +292,13 @@ public enum Action
* with the encryption parameters specified in the 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/single/issue}
+ * API is called with {@code denied} set to {@code true}).
+ *
+ *
* @since 3.86
*/
BAD_REQUEST,
From 0a7a7fd40395bfeaa5ca6e5e57c30531c4a38710 Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Sat, 10 Jan 2026 14:29:52 +0900
Subject: [PATCH 069/132] Updated CHANGES files for version 4.35.
---
CHANGES.ja.md | 3 +++
CHANGES.md | 3 +++
2 files changed, 6 insertions(+)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index fadd1fa75..10a058c20 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,9 @@
変更点
======
+4.35 (2026 年 01 月 10 日)
+--------------------------
+
- `CredentialDeferredIssueRequest` クラス
* `isDenied()` メソッドを追加。
* `setDenied(boolean)` メソッドを追加。
diff --git a/CHANGES.md b/CHANGES.md
index 0df2b86e1..dce085556 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,9 @@
CHANGES
=======
+4.35 (2026-01-10)
+-----------------
+
- `CredentialDeferredIssueRequest` class
* Added the `isDenied()` method.
* Added the `setDenied(boolean)` method.
From 2c323ec73f0e9175b0c2e848c376ae1c647ddd3c Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Sat, 10 Jan 2026 05:32:50 +0000
Subject: [PATCH 070/132] [maven-release-plugin] prepare release
authlete-java-common-4.35
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 3fb1ac042..688da7582 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.35-SNAPSHOT
+ 4.35
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.21
+ authlete-java-common-4.35
From a5b5c8281bd1596c8632d9544de079d5ddeb9290 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Sat, 10 Jan 2026 05:32:51 +0000
Subject: [PATCH 071/132] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 688da7582..b253790a5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.35
+ 4.36-SNAPSHOT
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.35
+ authlete-java-common-4.21
From 4fca9314dfc68708c371dff4a6a749209e466415 Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Thu, 26 Feb 2026 17:07:06 +0900
Subject: [PATCH 072/132] Update the JavaDoc of
CredentialRequestInfo.getDetails()
---
.../authlete/common/dto/CredentialRequestInfo.java | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/src/main/java/com/authlete/common/dto/CredentialRequestInfo.java b/src/main/java/com/authlete/common/dto/CredentialRequestInfo.java
index eb9fc9582..7f0a6d020 100644
--- a/src/main/java/com/authlete/common/dto/CredentialRequestInfo.java
+++ b/src/main/java/com/authlete/common/dto/CredentialRequestInfo.java
@@ -270,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:
*
*
+ *
+ * {@code credential_configuration_id}
+ * {@code credential_identifier}
+ * {@code credential_response_encryption}
+ * {@code format}
+ * {@code proof}
+ * {@code proofs}
+ *
+ *
*
* For example, when the original credential request holds the following:
*
From aea1dfa2024701df06d1c262c02e57f2fd6ca470 Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Thu, 26 Feb 2026 17:12:15 +0900
Subject: [PATCH 073/132] feat: OAuth SPIFFE Client Authentication (#156)
---
CHANGES.ja.md | 10 ++
CHANGES.md | 10 ++
.../java/com/authlete/common/dto/Client.java | 166 +++++++++++++++++-
.../common/types/ClientAuthMethod.java | 18 +-
4 files changed, 201 insertions(+), 3 deletions(-)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index 10a058c20..0ea09dec8 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,16 @@
変更点
======
+- `ClientAuthMethod` 列挙型
+ * `SPIFFE_JWT` を追加。
+
+- `Client` クラス
+ * `getSpiffeId()` メソッドを追加。
+ * `setSpiffeId(URI)` メソッドを追加。
+ * `getSpiffeBundleEndpoint()` メソッドを追加。
+ * `setSpiffeBundleEndpoint(URI)` メソッドを追加。
+
+
4.35 (2026 年 01 月 10 日)
--------------------------
diff --git a/CHANGES.md b/CHANGES.md
index dce085556..3f3594524 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,16 @@
CHANGES
=======
+- `ClientAuthMethod` enum
+ * Added `SPIFFE_JWT`.
+
+- `Client` class
+ * Added the `getSpiffeId()` method.
+ * Added the `setSpiffeId(URI)` method.
+ * Added the `getSpiffeBundleEndpoint()` method.
+ * Added the `setSpiffeBundleEndpoint(URI)` method.
+
+
4.35 (2026-01-10)
-----------------
diff --git a/src/main/java/com/authlete/common/dto/Client.java b/src/main/java/com/authlete/common/dto/Client.java
index 9d6ec524d..e57fce8e2 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-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.
@@ -87,7 +87,7 @@
*/
public class Client implements Serializable
{
- private static final long serialVersionUID = 39L;
+ private static final long serialVersionUID = 40L;
/*
@@ -711,6 +711,24 @@ public class Client implements Serializable
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;
+
+
/**
* Get the client number.
*
@@ -5654,6 +5672,140 @@ else if (client.isDiscoveredByMetadataDocument())
}
+ /**
+ * Get the SPIFFE ID. This property corresponds to the {@code spiffe_id}
+ * client metadata.
+ *
+ *
+ * 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://example.com/workload/* ).
+ * When comparing it with the SPIFFE ID in the SVID, the wildcard is taken
+ * into account.
+ *
+ *
+ * @return
+ * The SPIFFE ID.
+ *
+ * @since 4.36
+ * @since Authlete 3.0.28
+ *
+ * @see SPIFFE
+ * @see
+ * OAuth SPIFFE Client Authentication
+ */
+ public URI getSpiffeId()
+ {
+ return spiffeId;
+ }
+
+
+ /**
+ * Set the SPIFFE ID. This property corresponds to the {@code spiffe_id}
+ * client metadata.
+ *
+ *
+ * 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://example.com/workload/* ).
+ * When comparing it with the SPIFFE ID in the SVID, the wildcard is taken
+ * into account.
+ *
+ *
+ * @param spiffeId
+ * The SPIFFE ID.
+ *
+ * @since 4.36
+ * @since Authlete 3.0.28
+ *
+ * @see SPIFFE
+ * @see
+ * OAuth SPIFFE Client Authentication
+ */
+ public Client setSpiffeId(URI spiffeId)
+ {
+ this.spiffeId = spiffeId;
+
+ return this;
+ }
+
+
+ /**
+ * Get 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.
+ *
+ *
+ * @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 a {@code Map} instance that represents a set of standard client
* metadata.
@@ -5947,6 +6099,16 @@ public Map toStandardMetadata(ClientMetadataControl control)
// response_modes
put(metadata, "response_modes", getResponseModes(), nullIncluded);
+ //----------------------------------------------------------------------
+ // OAuth SPIFFE Client Authentication
+ //----------------------------------------------------------------------
+
+ // spiffe_id
+ put(metadata, "spiffe_id", getSpiffeId(), nullIncluded);
+
+ // spiffe_bundle_endpoint
+ put(metadata, "spiffe_bundle_endpoint", getSpiffeBundleEndpoint(), nullIncluded);
+
//----------------------------------------------------------------------
// Custom Metadata
//----------------------------------------------------------------------
diff --git a/src/main/java/com/authlete/common/types/ClientAuthMethod.java b/src/main/java/com/authlete/common/types/ClientAuthMethod.java
index 56645e282..7a192dc9e 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),
;
From f071426374e9a4f8b54e439548234ce395d13fb6 Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Thu, 26 Feb 2026 17:13:30 +0900
Subject: [PATCH 074/132] Updated CHANGES files for version 4.36.
---
CHANGES.ja.md | 3 +++
CHANGES.md | 3 +++
2 files changed, 6 insertions(+)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index 0ea09dec8..c6d0f6812 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,9 @@
変更点
======
+4.36 (2026 年 02 月 26 日)
+--------------------------
+
- `ClientAuthMethod` 列挙型
* `SPIFFE_JWT` を追加。
diff --git a/CHANGES.md b/CHANGES.md
index 3f3594524..cb2c68a0e 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,9 @@
CHANGES
=======
+4.36 (2026-02-26)
+-----------------
+
- `ClientAuthMethod` enum
* Added `SPIFFE_JWT`.
From 09c020234e346de6892bb279562a234e0ea1c35c Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Thu, 26 Feb 2026 08:16:27 +0000
Subject: [PATCH 075/132] [maven-release-plugin] prepare release
authlete-java-common-4.36
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index b253790a5..bb5db0966 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.36-SNAPSHOT
+ 4.36
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.21
+ authlete-java-common-4.36
From a4321fb750063cc39da3cba6567667e896000681 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Thu, 26 Feb 2026 08:16:29 +0000
Subject: [PATCH 076/132] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index bb5db0966..6c57a4b8e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.36
+ 4.37-SNAPSHOT
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.36
+ authlete-java-common-4.21
From ee0619d93f891249fbf39afc799d26e1a8363fb1 Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Mon, 2 Mar 2026 06:45:18 +0900
Subject: [PATCH 077/132] Add JWT_SPIFFE to ClientAssertionType
---
CHANGES.ja.md | 4 ++++
CHANGES.md | 4 ++++
.../common/types/ClientAssertionType.java | 18 +++++++++++++++++-
3 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index c6d0f6812..35f50096c 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,10 @@
変更点
======
+- `ClientAssertionType` 列挙型
+ * `JWT_SPIFFE` を追加。
+
+
4.36 (2026 年 02 月 26 日)
--------------------------
diff --git a/CHANGES.md b/CHANGES.md
index cb2c68a0e..9d1c21c99 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,10 @@
CHANGES
=======
+- `ClientAssertionType` enum
+ * Added `JWT_SPIFFE`.
+
+
4.36 (2026-02-26)
-----------------
diff --git a/src/main/java/com/authlete/common/types/ClientAssertionType.java b/src/main/java/com/authlete/common/types/ClientAssertionType.java
index ce3f877ca..450777d9b 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"),
;
From db3ac3639167191b2aed8b1a66e3dc610f5834da Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Mon, 2 Mar 2026 06:46:39 +0900
Subject: [PATCH 078/132] Updated CHANGES files for version 4.37.
---
CHANGES.ja.md | 3 +++
CHANGES.md | 3 +++
2 files changed, 6 insertions(+)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index 35f50096c..d4514e419 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,9 @@
変更点
======
+4.37 (2026 年 03 月 02 日)
+--------------------------
+
- `ClientAssertionType` 列挙型
* `JWT_SPIFFE` を追加。
diff --git a/CHANGES.md b/CHANGES.md
index 9d1c21c99..339482b58 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,9 @@
CHANGES
=======
+4.37 (2026-03-02)
+-----------------
+
- `ClientAssertionType` enum
* Added `JWT_SPIFFE`.
From bc00dd7c0484603a45eb4a7113b32a27e3560fc2 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Sun, 1 Mar 2026 21:49:13 +0000
Subject: [PATCH 079/132] [maven-release-plugin] prepare release
authlete-java-common-4.37
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 6c57a4b8e..61cab954b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.37-SNAPSHOT
+ 4.37
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.21
+ authlete-java-common-4.37
From c5d9b10240283a338e04bb8b2ea38df1f67afc48 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Sun, 1 Mar 2026 21:49:14 +0000
Subject: [PATCH 080/132] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 61cab954b..afc24f35f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.37
+ 4.38-SNAPSHOT
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.37
+ authlete-java-common-4.21
From ba7d182db03181cc2257901103e772ee3aa74137 Mon Sep 17 00:00:00 2001
From: Hideki Ikeda
Date: Tue, 10 Mar 2026 12:34:32 +0900
Subject: [PATCH 081/132] feat: add refresh token identifiers to token update
request (#158)
* feat: add the 'refreshToken' parameter and the 'refreshTokenHash' parameter to TokenUpdateRequest
* Update CHANGES files for version 4.38.
* docs: update copyright year in TokenUpdateRequest
---
CHANGES.ja.md | 10 ++
CHANGES.md | 12 +-
.../common/dto/TokenUpdateRequest.java | 117 +++++++++++++++++-
3 files changed, 136 insertions(+), 3 deletions(-)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index d4514e419..a9e0db422 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,16 @@
変更点
======
+4.38 (2026 年 03 月 04 日)
+--------------------------
+
+- `TokenUpdateRequest` クラス
+ * `getRefreshToken()` メソッドを追加。
+ * `setRefreshToken(String)` メソッドを追加。
+ * `getRefreshTokenHash()` メソッドを追加。
+ * `setRefreshTokenHash(String)` メソッドを追加。
+
+
4.37 (2026 年 03 月 02 日)
--------------------------
diff --git a/CHANGES.md b/CHANGES.md
index 339482b58..837381e62 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,16 @@
CHANGES
=======
+4.38 (2026-03-04)
+-----------------
+
+- `TokenUpdateRequest` class
+ * Added the `getRefreshToken()` method.
+ * Added the `setRefreshToken(String)` method.
+ * Added the `getRefreshTokenHash()` method.
+ * Added the `setRefreshTokenHash(String)` method.
+
+
4.37 (2026-03-02)
-----------------
@@ -294,7 +304,7 @@ CHANGES
- `Client` class
* Added the `isInScopeForTokenMigration()` method.
* Added the `setInScopeForTokenMigration(boolean)` method.
-
+
- Removed `com.neovisionaries:nv-cipher` and `com.neovisionaries:nv-digest` dependencies.
diff --git a/src/main/java/com/authlete/common/dto/TokenUpdateRequest.java b/src/main/java/com/authlete/common/dto/TokenUpdateRequest.java
index 1435639af..a9afe9139 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;
+ }
}
From d24a20433ca7cddeeb10ad4a6adde2f25216bbe5 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Tue, 10 Mar 2026 03:37:43 +0000
Subject: [PATCH 082/132] [maven-release-plugin] prepare release
authlete-java-common-4.38
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index afc24f35f..164d3eb52 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.38-SNAPSHOT
+ 4.38
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.21
+ authlete-java-common-4.38
From 740af6d5a312ae02e13ce2c1dccdbb53f2ee8964 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Tue, 10 Mar 2026 03:37:45 +0000
Subject: [PATCH 083/132] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 164d3eb52..6981c7dde 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.38
+ 4.39-SNAPSHOT
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.38
+ authlete-java-common-4.21
From 63773368d63f158328f14d6ea235bec930e9d29f Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Fri, 20 Mar 2026 15:44:02 +0900
Subject: [PATCH 084/132] feat: Attestation Challenge (#160)
---
CHANGES.ja.md | 40 +++
CHANGES.md | 40 +++
README.ja.md | 4 +
README.md | 4 +
.../com/authlete/common/api/AuthleteApi.java | 42 ++-
.../authlete/common/api/AuthleteApiImpl.java | 20 +-
.../common/api/AuthleteApiImplV3.java | 17 +-
.../dto/AttestationChallengeRequest.java | 75 ++++++
.../dto/AttestationChallengeResponse.java | 249 ++++++++++++++++++
.../BackchannelAuthenticationResponse.java | 113 +++++++-
.../dto/DeviceAuthorizationResponse.java | 113 +++++++-
.../common/dto/PushedAuthReqResponse.java | 113 +++++++-
.../common/dto/RevocationResponse.java | 106 +++++++-
.../java/com/authlete/common/dto/Service.java | 119 ++++++++-
.../authlete/common/dto/TokenResponse.java | 108 +++++++-
.../com/authlete/common/types/ErrorCode.java | 41 ++-
16 files changed, 1188 insertions(+), 16 deletions(-)
create mode 100644 src/main/java/com/authlete/common/dto/AttestationChallengeRequest.java
create mode 100644 src/main/java/com/authlete/common/dto/AttestationChallengeResponse.java
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index a9e0db422..ad8abcd14 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,46 @@
変更点
======
+- `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 日)
--------------------------
diff --git a/CHANGES.md b/CHANGES.md
index 837381e62..88f82bb8e 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,46 @@
CHANGES
=======
+- `AuthleteApi` interface
+ * Added the `attestationChallenge(AttestationChallengeRequest)` method.
+ * Added the `attestationChallenge(AttestationChallengeRequest, Options)` method.
+
+- `BackchannelAuthenticationResponse` class
+ * Added the `getAttestationChallenge()` method.
+ * Added the `setAttestationChallenge(String)` method.
+
+- `DeviceAuthorizationResponse` class
+ * Added the `getAttestationChallenge()` method.
+ * Added the `setAttestationChallenge(String)` method.
+
+- `ErrorCode` enum
+ * Added `use_attestation_challenge`.
+ * Added `use_fresh_attestation`.
+ * Added `invalid_client_attestation`.
+
+- `PushedAuthReqResponse` class
+ * Added the `getAttestationChallenge()` method.
+ * Added the `setAttestationChallenge(String)` method.
+
+- `RevocationResponse` class
+ * Added the `getAttestationChallenge()` method.
+ * Added the `setAttestationChallenge(String)` method.
+
+- `Service` class
+ * Added the `getChallengeEndpoint()` method.
+ * Added the `setChallengeEndpoint(URI)` method.
+ * Added the `getAttestationChallengeTimeWindow()` method.
+ * Added the `setAttestationChallengeTimeWindow(long)` method.
+
+- `TokenResponse` class
+ * Added the `getAttestationChallenge()` method.
+ * Added the `setAttestationChallenge(String)` method.
+
+- New types
+ * `AttestationChallengeRequest` class
+ * `AttestationChallengeResponse` class
+
+
4.38 (2026-03-04)
-----------------
diff --git a/README.ja.md b/README.ja.md
index c4b6c09aa..4e3d3c287 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -369,6 +369,10 @@ settings.setReadTimeout(5000);
- `nativeSso(NativeSsoRequest)`
- `nativeSsoLogout(NativeSsoLogoutRequest)`
+ 23. OAuth 2.0 Attestation-Based Client Authentication に関するメソッド群
+
+ - `attestationChallenge(AttestationChallengeRequest)`
+
*例*
次のコードは既存のサービスのリストを取得する例です。
diff --git a/README.md b/README.md
index f298c74f6..b795cfbde 100644
--- a/README.md
+++ b/README.md
@@ -380,6 +380,10 @@ Methods in `AuthleteApi` interface can be divided into some categories.
- `nativeSso(NativeSsoRequest)`
- `nativeSsoLogout(NativeSsoLogoutRequest)`
+ 23. Methods for OAuth 2.0 Attestation-Based Client Authentication
+
+ - `attestationChallenge(AttestationChallengeRequest)`
+
*Examples*
The following code snippet is an example to get the list of your existing
diff --git a/src/main/java/com/authlete/common/api/AuthleteApi.java b/src/main/java/com/authlete/common/api/AuthleteApi.java
index 0b58e1459..6efee6b01 100644
--- a/src/main/java/com/authlete/common/api/AuthleteApi.java
+++ b/src/main/java/com/authlete/common/api/AuthleteApi.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.
@@ -23,6 +23,8 @@
import com.authlete.common.dto.AuthorizationIssueResponse;
import com.authlete.common.dto.AuthorizationRequest;
import com.authlete.common.dto.AuthorizationResponse;
+import com.authlete.common.dto.AttestationChallengeRequest;
+import com.authlete.common.dto.AttestationChallengeResponse;
import com.authlete.common.dto.AuthorizationTicketInfoRequest;
import com.authlete.common.dto.AuthorizationTicketInfoResponse;
import com.authlete.common.dto.AuthorizationTicketUpdateRequest;
@@ -4842,4 +4844,42 @@ NativeSsoResponse nativeSso(
*/
NativeSsoLogoutResponse nativeSsoLogout(
NativeSsoLogoutRequest request, Options options) throws AuthleteApiException;
+
+
+ /**
+ * Call Authlete's {@code /attestation/challenge} API.
+ *
+ * @param request
+ * Request parameters passed to the API.
+ *
+ * @return
+ * Response from the API.
+ *
+ * @since 4.39
+ * @since Authlete 3.0.28
+ */
+ default AttestationChallengeResponse attestationChallenge(
+ AttestationChallengeRequest request) throws AuthleteApiException
+ {
+ return attestationChallenge(request, null);
+ }
+
+
+ /**
+ * Call Authlete's {@code /attestation/challenge} API.
+ *
+ * @param request
+ * Request parameters passed to the API.
+ *
+ * @param options
+ * Request options.
+ *
+ * @return
+ * Response from the API.
+ *
+ * @since 4.39
+ * @since Authlete 3.0.28
+ */
+ AttestationChallengeResponse attestationChallenge(
+ AttestationChallengeRequest request, Options options) throws AuthleteApiException;
}
diff --git a/src/main/java/com/authlete/common/api/AuthleteApiImpl.java b/src/main/java/com/authlete/common/api/AuthleteApiImpl.java
index 3d8709d54..828efcb97 100644
--- a/src/main/java/com/authlete/common/api/AuthleteApiImpl.java
+++ b/src/main/java/com/authlete/common/api/AuthleteApiImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017-2025 Authlete, Inc.
+ * Copyright (C) 2017-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.
@@ -22,6 +22,8 @@
import com.authlete.common.conf.AuthleteApiVersion;
import com.authlete.common.conf.AuthleteConfiguration;
import com.authlete.common.dto.ApiResponse;
+import com.authlete.common.dto.AttestationChallengeRequest;
+import com.authlete.common.dto.AttestationChallengeResponse;
import com.authlete.common.dto.AuthorizationFailRequest;
import com.authlete.common.dto.AuthorizationFailResponse;
import com.authlete.common.dto.AuthorizationIssueRequest;
@@ -143,6 +145,7 @@
*/
class AuthleteApiImpl extends AuthleteApiBasicImpl
{
+ private static final String ATTESTATION_CHALLENGE_API_PATH = "/api/attestation/challenge";
private static final String AUTH_AUTHORIZATION_API_PATH = "/api/auth/authorization";
private static final String AUTH_AUTHORIZATION_FAIL_API_PATH = "/api/auth/authorization/fail";
private static final String AUTH_AUTHORIZATION_ISSUE_API_PATH = "/api/auth/authorization/issue";
@@ -1810,4 +1813,19 @@ public NativeSsoLogoutResponse nativeSsoLogout(
NATIVE_SSO_LOGOUT_API_PATH)
.setOptions(options));
}
+
+
+ @Override
+ public AttestationChallengeResponse attestationChallenge(
+ AttestationChallengeRequest request, Options options) throws AuthleteApiException
+ {
+ // Note that the /attestation/challenge API is not available in Authlete 2.x,
+ // so the executeApiCall below will throw an exception.
+
+ return executeApiCall(
+ new ServicePostApiCaller(
+ AttestationChallengeResponse.class, request,
+ ATTESTATION_CHALLENGE_API_PATH)
+ .setOptions(options));
+ }
}
diff --git a/src/main/java/com/authlete/common/api/AuthleteApiImplV3.java b/src/main/java/com/authlete/common/api/AuthleteApiImplV3.java
index cd34b2b86..99866e261 100644
--- a/src/main/java/com/authlete/common/api/AuthleteApiImplV3.java
+++ b/src/main/java/com/authlete/common/api/AuthleteApiImplV3.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2025 Authlete, Inc.
+ * 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.
@@ -23,6 +23,8 @@
import com.authlete.common.conf.AuthleteApiVersion;
import com.authlete.common.conf.AuthleteConfiguration;
import com.authlete.common.dto.ApiResponse;
+import com.authlete.common.dto.AttestationChallengeRequest;
+import com.authlete.common.dto.AttestationChallengeResponse;
import com.authlete.common.dto.AuthorizationFailRequest;
import com.authlete.common.dto.AuthorizationFailResponse;
import com.authlete.common.dto.AuthorizationIssueRequest;
@@ -144,6 +146,7 @@
*/
public class AuthleteApiImplV3 extends AuthleteApiBasicImpl
{
+ private static final String ATTESTATION_CHALLENGE_API_PATH = "/api/%d/attestation/challenge";
private static final String AUTH_AUTHORIZATION_API_PATH = "/api/%d/auth/authorization";
private static final String AUTH_AUTHORIZATION_FAIL_API_PATH = "/api/%d/auth/authorization/fail";
private static final String AUTH_AUTHORIZATION_ISSUE_API_PATH = "/api/%d/auth/authorization/issue";
@@ -1667,4 +1670,16 @@ public NativeSsoLogoutResponse nativeSsoLogout(
NATIVE_SSO_LOGOUT_API_PATH, mServiceId)
.setOptions(options));
}
+
+
+ @Override
+ public AttestationChallengeResponse attestationChallenge(
+ AttestationChallengeRequest request, Options options) throws AuthleteApiException
+ {
+ return executeApiCall(
+ new PostApiCaller(
+ AttestationChallengeResponse.class, request,
+ ATTESTATION_CHALLENGE_API_PATH, mServiceId)
+ .setOptions(options));
+ }
}
diff --git a/src/main/java/com/authlete/common/dto/AttestationChallengeRequest.java b/src/main/java/com/authlete/common/dto/AttestationChallengeRequest.java
new file mode 100644
index 000000000..6c89203fd
--- /dev/null
+++ b/src/main/java/com/authlete/common/dto/AttestationChallengeRequest.java
@@ -0,0 +1,75 @@
+/*
+ * 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 {@code /attestation/challenge} API.
+ *
+ *
+ * The Authlete API is supposed to be used from within the implementation of
+ * the challenge endpoint of the authorization server.
+ *
+ *
+ * @since 4.39
+ * @since Authlete 3.0.28
+ *
+ * @see
+ * OAuth 2.0 Attestation-Based Client Authentication
+ */
+public class AttestationChallengeRequest implements Serializable
+{
+ private static final long serialVersionUID = 1L;
+
+
+ private boolean pretty;
+
+
+
+ /**
+ * Get the flag indicating whether the challenge response is written in the
+ * pretty format or not.
+ *
+ * @return
+ * {@code true} if the challenge response is written in the pretty
+ * format.
+ */
+ public boolean isPretty()
+ {
+ return pretty;
+ }
+
+
+ /**
+ * Set the flag indicating whether the challenge response is written in the
+ * pretty format or not.
+ *
+ * @param pretty
+ * {@code true} to write the challenge response in the pretty format.
+ *
+ * @return
+ * {@code this} object.
+ */
+ public AttestationChallengeRequest setPretty(boolean pretty)
+ {
+ this.pretty = pretty;
+
+ return this;
+ }
+}
diff --git a/src/main/java/com/authlete/common/dto/AttestationChallengeResponse.java b/src/main/java/com/authlete/common/dto/AttestationChallengeResponse.java
new file mode 100644
index 000000000..b3ad5a306
--- /dev/null
+++ b/src/main/java/com/authlete/common/dto/AttestationChallengeResponse.java
@@ -0,0 +1,249 @@
+/*
+ * 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;
+
+
+/**
+ * Response from Authlete's {@code /attestation/challenge} API.
+ *
+ *
+ * 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 {@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 challenge response successfully.
+ *
+ *
+ *
+ * In this case, the implementation of the challenge endpoint of the
+ * authorization server 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 /attestation/challenge} API and it is available as the
+ * {@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.
+ *
+ *
+ *
+ * In this case, 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}". The message body (= error
+ * information in the JSON format) of the response has been prepared by Authlete's
+ * {@code /attestation/challenge} API and it is available as the
+ * {@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/BackchannelAuthenticationResponse.java b/src/main/java/com/authlete/common/dto/BackchannelAuthenticationResponse.java
index cc18e1c12..0828ba201 100644
--- a/src/main/java/com/authlete/common/dto/BackchannelAuthenticationResponse.java
+++ b/src/main/java/com/authlete/common/dto/BackchannelAuthenticationResponse.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2025 Authlete, Inc.
+ * Copyright (C) 2018-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
@@ -594,11 +594,39 @@
*
*
*
+ *
+ *
+ * Attestation Challenge (Authlete 3.0.28 onwards)
+ *
+ *
+ * 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 = 12L;
+ private static final long serialVersionUID = 13L;
/**
@@ -828,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
@@ -2349,4 +2387,75 @@ public BackchannelAuthenticationResponse setTicket(String ticket)
return this;
}
+
+
+ /**
+ * 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
+ * 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/DeviceAuthorizationResponse.java b/src/main/java/com/authlete/common/dto/DeviceAuthorizationResponse.java
index 423c0995f..18bd97bcf 100644
--- a/src/main/java/com/authlete/common/dto/DeviceAuthorizationResponse.java
+++ b/src/main/java/com/authlete/common/dto/DeviceAuthorizationResponse.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019-2025 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. You may obtain a copy of
@@ -166,11 +166,39 @@
*
*
*
+ *
+ *
+ * Attestation Challenge (Authlete 3.0.28 onwards)
+ *
+ *
+ * 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 = 10L;
+ private static final long serialVersionUID = 11L;
/**
@@ -370,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
@@ -1667,4 +1705,75 @@ public DeviceAuthorizationResponse setClientAttributes(Pair[] attributes)
return this;
}
+
+
+ /**
+ * 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
+ * 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/PushedAuthReqResponse.java b/src/main/java/com/authlete/common/dto/PushedAuthReqResponse.java
index 467f1e738..33b699756 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.
*
*
+ *
+ *
+ * Attestation Challenge (Authlete 3.0.28 onwards)
+ *
+ *
+ * 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 9d1e96305..b97867cf3 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 @@
*
*
*
+ *
+ *
+ * Attestation Challenge (Authlete 3.0.28 onwards)
+ *
+ *
+ * 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 9bd957527..67bf8076a 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 = 89L;
+ private static final long serialVersionUID = 90L;
/*
@@ -1981,6 +1981,25 @@ public class Service implements Serializable
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;
+
+
/**
* Get the service number.
*
@@ -13021,4 +13040,100 @@ public Service setHttpAliasProhibited(boolean 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;
+ }
}
diff --git a/src/main/java/com/authlete/common/dto/TokenResponse.java b/src/main/java/com/authlete/common/dto/TokenResponse.java
index 17bf78bb3..16fc38d69 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.
*
*
+ *
+ * Attestation Challenge (Authlete 3.0.28 onwards)
+ *
+ *
+ * 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 = 22L;
+ private static final long serialVersionUID = 23L;
/**
@@ -1636,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.
@@ -3836,4 +3876,68 @@ public void setDeviceSecretHash(String deviceSecretHash)
{
this.deviceSecretHash = deviceSecretHash;
}
+
+
+ /**
+ * 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
+ * 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/types/ErrorCode.java b/src/main/java/com/authlete/common/types/ErrorCode.java
index 2988d3314..76cc10bca 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-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.
@@ -704,5 +704,44 @@ public enum ErrorCode
* @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,
;
}
From 7e0f3ad983f317f17114dfa573c5d4d314d7e836 Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Fri, 20 Mar 2026 15:45:40 +0900
Subject: [PATCH 085/132] Updated CHANGES files for version 4.39.
---
CHANGES.ja.md | 3 +++
CHANGES.md | 3 +++
2 files changed, 6 insertions(+)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index ad8abcd14..ed99f2ab6 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,9 @@
変更点
======
+4.39 (2026 年 03 月 20 日)
+--------------------------
+
- `AuthleteApi` インターフェース
* `attestationChallenge(AttestationChallengeRequest)` メソッドを追加。
* `attestationChallenge(AttestationChallengeRequest, Options)` メソッドを追加。
diff --git a/CHANGES.md b/CHANGES.md
index 88f82bb8e..44e46cf9e 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,9 @@
CHANGES
=======
+4.39 (2026-03-20)
+-----------------
+
- `AuthleteApi` interface
* Added the `attestationChallenge(AttestationChallengeRequest)` method.
* Added the `attestationChallenge(AttestationChallengeRequest, Options)` method.
From a05b60c14fb63a13a0ad6d1c08b975114722c1a7 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Fri, 20 Mar 2026 06:47:53 +0000
Subject: [PATCH 086/132] [maven-release-plugin] prepare release
authlete-java-common-4.39
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 6981c7dde..e284b2bd9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.39-SNAPSHOT
+ 4.39
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.21
+ authlete-java-common-4.39
From 4b2942b67901ab1044f97b58cb8e9f0b9fd0ec22 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Fri, 20 Mar 2026 06:47:55 +0000
Subject: [PATCH 087/132] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index e284b2bd9..7dbf79226 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.39
+ 4.40-SNAPSHOT
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.39
+ authlete-java-common-4.21
From c41491273be30b94409bb9d39d50cd9a7e2617d2 Mon Sep 17 00:00:00 2001
From: sshehrozali
Date: Tue, 24 Mar 2026 12:59:13 +0500
Subject: [PATCH 088/132] feat: added authorizationCodeDuration field in
Service DTO
---
.../java/com/authlete/common/dto/Service.java | 35 +++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/src/main/java/com/authlete/common/dto/Service.java b/src/main/java/com/authlete/common/dto/Service.java
index 67bf8076a..d3bd2809e 100644
--- a/src/main/java/com/authlete/common/dto/Service.java
+++ b/src/main/java/com/authlete/common/dto/Service.java
@@ -738,6 +738,12 @@ public class Service implements Serializable
private long idTokenDuration;
+ /**
+ * Duration of authorization code in seconds.
+ */
+ private Long authorizationCodeDuration;
+
+
/**
* Duration of authorization response JWTs.
*
@@ -3088,6 +3094,35 @@ public Service setIdTokenDuration(long duration)
}
+ /**
+ * Get the duration of authorization code in seconds.
+ *
+ * @return
+ * The duration of authorization code in seconds.
+ */
+ 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.
+ */
+ public Service setAuthorizationCodeDuration(Long duration)
+ {
+ this.authorizationCodeDuration = duration;
+
+ return this;
+ }
+
+
/**
* Get the duration of authorization response JWTs.
*
From 93f748dd0854958fce58f19728c9fa4b60018754 Mon Sep 17 00:00:00 2001
From: sshehrozali
Date: Wed, 25 Mar 2026 11:56:19 +0500
Subject: [PATCH 089/132] feat: added javadocs and changed to primitive long
type
---
src/main/java/com/authlete/common/dto/Service.java | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/src/main/java/com/authlete/common/dto/Service.java b/src/main/java/com/authlete/common/dto/Service.java
index d3bd2809e..ce02a3260 100644
--- a/src/main/java/com/authlete/common/dto/Service.java
+++ b/src/main/java/com/authlete/common/dto/Service.java
@@ -740,8 +740,13 @@ public class Service implements Serializable
/**
* 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.
+ *
*/
- private Long authorizationCodeDuration;
+ private long authorizationCodeDuration;
/**
@@ -3100,7 +3105,7 @@ public Service setIdTokenDuration(long duration)
* @return
* The duration of authorization code in seconds.
*/
- public Long getAuthorizationCodeDuration()
+ public long getAuthorizationCodeDuration()
{
return authorizationCodeDuration;
}
@@ -3115,7 +3120,7 @@ public Long getAuthorizationCodeDuration()
* @return
* {@code this} object.
*/
- public Service setAuthorizationCodeDuration(Long duration)
+ public Service setAuthorizationCodeDuration(long duration)
{
this.authorizationCodeDuration = duration;
From e027b9b49a7b6077b29a536b958140a0a1c2a6d0 Mon Sep 17 00:00:00 2001
From: sshehrozali
Date: Wed, 25 Mar 2026 11:59:56 +0500
Subject: [PATCH 090/132] feat: added fapi2 note in javadocs
---
src/main/java/com/authlete/common/dto/Service.java | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/main/java/com/authlete/common/dto/Service.java b/src/main/java/com/authlete/common/dto/Service.java
index ce02a3260..df474e949 100644
--- a/src/main/java/com/authlete/common/dto/Service.java
+++ b/src/main/java/com/authlete/common/dto/Service.java
@@ -745,6 +745,11 @@ public class Service implements Serializable
* 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.
+ *
*/
private long authorizationCodeDuration;
From 279bd8fa65f5580af8df7dc16ffd957d9a9ef5b4 Mon Sep 17 00:00:00 2001
From: sshehrozali
Date: Wed, 25 Mar 2026 12:20:24 +0500
Subject: [PATCH 091/132] feat: updated CHANGES.md and CHANGES.ja.md
---
CHANGES.ja.md | 8 ++++++++
CHANGES.md | 8 ++++++++
2 files changed, 16 insertions(+)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index ed99f2ab6..bd58588c9 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,14 @@
変更点
======
+4.40 (2026 年 03 月 24 日)
+--------------------------
+
+- `Service` クラス
+ * `getAuthorizationCodeDuration()` メソッドを追加。
+ * `setAuthorizationCodeDuration(long)` メソッドを追加。
+
+
4.39 (2026 年 03 月 20 日)
--------------------------
diff --git a/CHANGES.md b/CHANGES.md
index 44e46cf9e..e185b0ac2 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,14 @@
CHANGES
=======
+4.40 (2026-03-24)
+-----------------
+
+- `Service` class
+ * Added the `getAuthorizationCodeDuration()` method.
+ * Added the `setAuthorizationCodeDuration(long)` method.
+
+
4.39 (2026-03-20)
-----------------
From 7d2bcc8f1688ffea011c2a7931c5e2245ef26c7a Mon Sep 17 00:00:00 2001
From: sshehrozali
Date: Wed, 25 Mar 2026 12:36:44 +0500
Subject: [PATCH 092/132] feat: added tag version
---
src/main/java/com/authlete/common/dto/Service.java | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/src/main/java/com/authlete/common/dto/Service.java b/src/main/java/com/authlete/common/dto/Service.java
index df474e949..9bbb8d27f 100644
--- a/src/main/java/com/authlete/common/dto/Service.java
+++ b/src/main/java/com/authlete/common/dto/Service.java
@@ -750,6 +750,8 @@ public class Service implements Serializable
* For FAPI 2.0, the lifetime of authorization codes is capped to 60
* seconds. See OpenID FAPI 2.0 Security Profile for details.
*
+ *
+ * @since Authlete 3.0.29
*/
private long authorizationCodeDuration;
@@ -3109,6 +3111,8 @@ public Service setIdTokenDuration(long duration)
*
* @return
* The duration of authorization code in seconds.
+ *
+ * @since 3.0.29
*/
public long getAuthorizationCodeDuration()
{
@@ -3124,6 +3128,8 @@ public long getAuthorizationCodeDuration()
*
* @return
* {@code this} object.
+ *
+ * @since 3.0.29
*/
public Service setAuthorizationCodeDuration(long duration)
{
From ac2df0bca08af880cf2496d51b653ebe8640db7c Mon Sep 17 00:00:00 2001
From: sshehrozali
Date: Wed, 25 Mar 2026 14:55:25 +0500
Subject: [PATCH 093/132] feat: added tag version
---
src/main/java/com/authlete/common/dto/Service.java | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/main/java/com/authlete/common/dto/Service.java b/src/main/java/com/authlete/common/dto/Service.java
index 9bbb8d27f..4da33e318 100644
--- a/src/main/java/com/authlete/common/dto/Service.java
+++ b/src/main/java/com/authlete/common/dto/Service.java
@@ -752,6 +752,7 @@ public class Service implements Serializable
*
*
* @since Authlete 3.0.29
+ * @since 4.40
*/
private long authorizationCodeDuration;
From deca933585f4d855fae0e9a5f01159ba4ec30491 Mon Sep 17 00:00:00 2001
From: sshehrozali
Date: Wed, 25 Mar 2026 15:08:45 +0500
Subject: [PATCH 094/132] feat: swapped lines
---
src/main/java/com/authlete/common/dto/Service.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/com/authlete/common/dto/Service.java b/src/main/java/com/authlete/common/dto/Service.java
index 4da33e318..c4b8352f6 100644
--- a/src/main/java/com/authlete/common/dto/Service.java
+++ b/src/main/java/com/authlete/common/dto/Service.java
@@ -751,8 +751,8 @@ public class Service implements Serializable
* seconds. See OpenID FAPI 2.0 Security Profile for details.
*
*
- * @since Authlete 3.0.29
* @since 4.40
+ * @since Authlete 3.0.29
*/
private long authorizationCodeDuration;
From a88323f6f5e0ebeba686e9c205d408b5f7e7253d Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Fri, 27 Mar 2026 08:37:54 +0000
Subject: [PATCH 095/132] [maven-release-plugin] prepare release
authlete-java-common-4.40
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 7dbf79226..25c205a49 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.40-SNAPSHOT
+ 4.40
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.21
+ authlete-java-common-4.40
From 57ff5fdfabd52ece8040bbebfb87f2068d75b79e Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Fri, 27 Mar 2026 08:37:56 +0000
Subject: [PATCH 096/132] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 25c205a49..a7bff33cf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.40
+ 4.41-SNAPSHOT
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.40
+ authlete-java-common-4.21
From c055737fada1af5d2057044b20fde014760a1da8 Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Sat, 28 Mar 2026 14:02:44 +0900
Subject: [PATCH 097/132] feat: HAIP version (#162)
---
CHANGES.ja.md | 9 ++
CHANGES.md | 9 ++
.../java/com/authlete/common/dto/Client.java | 90 ++++++++++++++++++-
.../java/com/authlete/common/dto/Service.java | 90 ++++++++++++++++++-
4 files changed, 196 insertions(+), 2 deletions(-)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index bd58588c9..fe1f89e0f 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,15 @@
変更点
======
+- `Client` クラス
+ * `getHaipVersion()` メソッドを追加。
+ * `setHaipVersion(String)` メソッドを追加。
+
+- `Service` クラス
+ * `getHaipVersion()` メソッドを追加。
+ * `setHaipVersion(String)` メソッドを追加。
+
+
4.40 (2026 年 03 月 24 日)
--------------------------
diff --git a/CHANGES.md b/CHANGES.md
index e185b0ac2..6cb9fe680 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,15 @@
CHANGES
=======
+- `Client` class
+ * Added the `getHaipVersion()` method.
+ * Added the `setHaipVersion(String)` method.
+
+- `Service` class
+ * Added the `getHaipVersion()` method.
+ * Added the `setHaipVersion(String)` method.
+
+
4.40 (2026-03-24)
-----------------
diff --git a/src/main/java/com/authlete/common/dto/Client.java b/src/main/java/com/authlete/common/dto/Client.java
index e57fce8e2..5d372b2fe 100644
--- a/src/main/java/com/authlete/common/dto/Client.java
+++ b/src/main/java/com/authlete/common/dto/Client.java
@@ -87,7 +87,7 @@
*/
public class Client implements Serializable
{
- private static final long serialVersionUID = 40L;
+ private static final long serialVersionUID = 41L;
/*
@@ -729,6 +729,15 @@ public class Client implements Serializable
private URI spiffeBundleEndpoint;
+ /**
+ * The HAIP version to comply with.
+ *
+ * @since 4.41
+ * @since Authlete 3.0.29
+ */
+ private String haipVersion;
+
+
/**
* Get the client number.
*
@@ -5806,6 +5815,85 @@ public Client setSpiffeBundleEndpoint(URI endpoint)
}
+ /**
+ * 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 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 3.0.29
+ *
+ * @see
+ * OpenID4VC High Assurance Interoperability Profile 1.0
+ */
+ public Client setHaipVersion(String version)
+ {
+ this.haipVersion = version;
+
+ return this;
+ }
+
+
/**
* Get a {@code Map} instance that represents a set of standard client
* metadata.
diff --git a/src/main/java/com/authlete/common/dto/Service.java b/src/main/java/com/authlete/common/dto/Service.java
index c4b8352f6..4c18c9bad 100644
--- a/src/main/java/com/authlete/common/dto/Service.java
+++ b/src/main/java/com/authlete/common/dto/Service.java
@@ -330,7 +330,7 @@
*/
public class Service implements Serializable
{
- private static final long serialVersionUID = 90L;
+ private static final long serialVersionUID = 91L;
/*
@@ -2019,6 +2019,15 @@ public class Service implements Serializable
private long attestationChallengeTimeWindow;
+ /**
+ * The HAIP version to comply with.
+ *
+ * @since 4.41
+ * @since Authlete 3.0.29
+ */
+ private String haipVersion;
+
+
/**
* Get the service number.
*
@@ -13188,4 +13197,83 @@ public Service setAttestationChallengeTimeWindow(long 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;
+ }
}
From 5696f6459c8842511665186d912c9d9ce74913a1 Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Sat, 28 Mar 2026 14:04:02 +0900
Subject: [PATCH 098/132] Updated CHANGES files for version 4.41.
---
CHANGES.ja.md | 3 +++
CHANGES.md | 3 +++
2 files changed, 6 insertions(+)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index fe1f89e0f..e02511628 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,9 @@
変更点
======
+4.41 (2026 年 03 月 28 日)
+--------------------------
+
- `Client` クラス
* `getHaipVersion()` メソッドを追加。
* `setHaipVersion(String)` メソッドを追加。
diff --git a/CHANGES.md b/CHANGES.md
index 6cb9fe680..f1848e4cb 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,9 @@
CHANGES
=======
+4.41 (2026-03-28)
+-----------------
+
- `Client` class
* Added the `getHaipVersion()` method.
* Added the `setHaipVersion(String)` method.
From 1c13421d9ec46648121ee99e770dbf287cb633f5 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Sat, 28 Mar 2026 05:06:40 +0000
Subject: [PATCH 099/132] [maven-release-plugin] prepare release
authlete-java-common-4.41
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index a7bff33cf..255f593ba 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.41-SNAPSHOT
+ 4.41
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.21
+ authlete-java-common-4.41
From b5d9d753cfb82c686c7f660715cf9182f98b7146 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Sat, 28 Mar 2026 05:06:42 +0000
Subject: [PATCH 100/132] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 255f593ba..7fc1d8799 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.41
+ 4.42-SNAPSHOT
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.41
+ authlete-java-common-4.21
From 4d29a1ae2ef27cd2354c6f3608b3bc041a6b73ad Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Tue, 14 Apr 2026 23:18:29 +0900
Subject: [PATCH 101/132] feat: Attester Roots (#163)
---
CHANGES.ja.md | 15 +
CHANGES.md | 15 +
.../java/com/authlete/common/dto/Service.java | 512 +++++++++++++++++-
3 files changed, 541 insertions(+), 1 deletion(-)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index e02511628..14e20d4dd 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,21 @@
変更点
======
+- `Service` クラス
+ * `getClientAttesterRoots()` メソッドを追加。
+ * `setClientAttesterRoots(String[])` メソッドを追加。
+ * `isClientAttesterRootsEnabled()` メソッドを追加。
+ * `setClientAttesterRootsEnabled(boolean)` メソッドを追加。
+ * `isClientAttesterRootsOnly()` メソッドを追加。
+ * `setClientAttesterRootsOnly(boolean)` メソッドを追加。
+ * `getKeyAttesterRoots()` メソッドを追加。
+ * `setKeyAttesterRoots(String[])` メソッドを追加。
+ * `isKeyAttesterRootsEnabled()` メソッドを追加。
+ * `setKeyAttesterRootsEnabled(boolean)` メソッドを追加。
+ * `isKeyAttesterRootsOnly()` メソッドを追加。
+ * `setKeyAttesterRootsOnly(boolean)` メソッドを追加。
+
+
4.41 (2026 年 03 月 28 日)
--------------------------
diff --git a/CHANGES.md b/CHANGES.md
index f1848e4cb..7ac7f8081 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,21 @@
CHANGES
=======
+- `Service` class
+ * Added the `getClientAttesterRoots()` method.
+ * Added the `setClientAttesterRoots(String[])` method.
+ * Added the `isClientAttesterRootsEnabled()` method.
+ * Added the `setClientAttesterRootsEnabled(boolean)` method.
+ * Added the `isClientAttesterRootsOnly()` method.
+ * Added the `setClientAttesterRootsOnly(boolean)` method.
+ * Added the `getKeyAttesterRoots()` method.
+ * Added the `setKeyAttesterRoots(String[])` method.
+ * Added the `isKeyAttesterRootsEnabled()` method.
+ * Added the `setKeyAttesterRootsEnabled(boolean)` method.
+ * Added the `isKeyAttesterRootsOnly()` method.
+ * Added the `setKeyAttesterRootsOnly(boolean)` method.
+
+
4.41 (2026-03-28)
-----------------
diff --git a/src/main/java/com/authlete/common/dto/Service.java b/src/main/java/com/authlete/common/dto/Service.java
index 4c18c9bad..543136484 100644
--- a/src/main/java/com/authlete/common/dto/Service.java
+++ b/src/main/java/com/authlete/common/dto/Service.java
@@ -330,7 +330,7 @@
*/
public class Service implements Serializable
{
- private static final long serialVersionUID = 91L;
+ private static final long serialVersionUID = 92L;
/*
@@ -2028,6 +2028,62 @@ public class Service implements Serializable
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;
+
+
/**
* Get the service number.
*
@@ -13276,4 +13332,458 @@ public Service setHaipVersion(String 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;
+ }
}
From 9dd7897ef3787d490df9e39890477393d3874962 Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Tue, 14 Apr 2026 23:19:47 +0900
Subject: [PATCH 102/132] Updated CHANGES files for version 4.42.
---
CHANGES.ja.md | 3 +++
CHANGES.md | 3 +++
2 files changed, 6 insertions(+)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index 14e20d4dd..1616355f4 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,9 @@
変更点
======
+4.42 (2026 年 04 月 14 日)
+--------------------------
+
- `Service` クラス
* `getClientAttesterRoots()` メソッドを追加。
* `setClientAttesterRoots(String[])` メソッドを追加。
diff --git a/CHANGES.md b/CHANGES.md
index 7ac7f8081..241ae1464 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,9 @@
CHANGES
=======
+4.42 (2026-04-14)
+-----------------
+
- `Service` class
* Added the `getClientAttesterRoots()` method.
* Added the `setClientAttesterRoots(String[])` method.
From 179c393d173c8c81e0752ec733e5d43b6ebb8eb7 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Tue, 14 Apr 2026 14:22:33 +0000
Subject: [PATCH 103/132] [maven-release-plugin] prepare release
authlete-java-common-4.42
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 7fc1d8799..19f6e0377 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.42-SNAPSHOT
+ 4.42
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.21
+ authlete-java-common-4.42
From bd9c58cde77cca7e600a94428ab3a4db9e49acaf Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Tue, 14 Apr 2026 14:22:35 +0000
Subject: [PATCH 104/132] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 19f6e0377..923ca23ba 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.42
+ 4.43-SNAPSHOT
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.42
+ authlete-java-common-4.21
From eea19660be807f79500984a04c09bb373e03d8bc Mon Sep 17 00:00:00 2001
From: Otso Nuortimo
Date: Fri, 13 Feb 2026 10:48:36 +0900
Subject: [PATCH 105/132] Add USE_INTROSPECTION access right and update
USE_SERVICE
---
src/main/java/com/authlete/common/api/AccessRight.java | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/main/java/com/authlete/common/api/AccessRight.java b/src/main/java/com/authlete/common/api/AccessRight.java
index 589738723..59a595ed7 100644
--- a/src/main/java/com/authlete/common/api/AccessRight.java
+++ b/src/main/java/com/authlete/common/api/AccessRight.java
@@ -49,8 +49,10 @@ public enum AccessRight
MODIFY_CLIENT(true, true, VIEW_CLIENT),
/** can view the details of this service */
VIEW_SERVICE(false, true, VIEW_CLIENT),
+ /** can use the introspection endpoint */
+ USE_INTROSPECTION(false, true),
/** can use the non-destructive service API calls (auth endpoint, token endpoint, etc.) */
- USE_SERVICE(false, true, VIEW_SERVICE, VIEW_CLIENT),
+ USE_SERVICE(false, true, USE_INTROSPECTION, VIEW_SERVICE, VIEW_CLIENT),
/** can create new clients on this service */
CREATE_CLIENT(false, true, USE_SERVICE, VIEW_SERVICE, MODIFY_CLIENT, VIEW_CLIENT),
/** can modify this service */
From d3fc08e34faaeb8c7ba17c899810510043883417 Mon Sep 17 00:00:00 2001
From: Otso Nuortimo
Date: Fri, 27 Mar 2026 19:53:49 +0900
Subject: [PATCH 106/132] feat: add VIEW_AUDIT_LOG access right
---
src/main/java/com/authlete/common/api/AccessRight.java | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/main/java/com/authlete/common/api/AccessRight.java b/src/main/java/com/authlete/common/api/AccessRight.java
index 59a595ed7..46f23b54d 100644
--- a/src/main/java/com/authlete/common/api/AccessRight.java
+++ b/src/main/java/com/authlete/common/api/AccessRight.java
@@ -61,6 +61,8 @@ public enum AccessRight
VIEW_DEFAULT_SERVICE(false, false),
/** can create additional services */
CREATE_SERVICE(false, false, VIEW_DEFAULT_SERVICE),
+ /** can view audit logs */
+ VIEW_AUDIT_LOG(false, false),
/** can delete a specific service */
DELETE_SERVICE(false, true),
/** can call administrative functions on the Authlete server */
From 6f43f5275e9c1f0424e0dfcf2f119f9a63fb4b82 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Fri, 17 Apr 2026 06:59:54 +0000
Subject: [PATCH 107/132] [maven-release-plugin] prepare release
authlete-java-common-4.43
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 923ca23ba..51138e2b2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.43-SNAPSHOT
+ 4.43
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.21
+ authlete-java-common-4.43
From 6d5409ebb1032fa654c8451ac1ef07a328cd1352 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Fri, 17 Apr 2026 06:59:57 +0000
Subject: [PATCH 108/132] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 51138e2b2..f79582923 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.43
+ 4.44-SNAPSHOT
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.43
+ authlete-java-common-4.21
From 6590a777b48bdbd89cbdfd58bbf5bfbbdb0a5740 Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Sun, 10 May 2026 14:56:32 +0900
Subject: [PATCH 109/132] feat: OpenID Connect Back-Channel Logout 1.0 (#165)
---
CHANGES.ja.md | 21 ++
CHANGES.md | 21 ++
.../com/authlete/common/api/AuthleteApi.java | 40 +++
.../authlete/common/api/AuthleteApiImpl.java | 18 ++
.../common/api/AuthleteApiImplV3.java | 15 ++
.../common/dto/AuthorizationIssueRequest.java | 74 ++++--
.../dto/BackchannelLogoutTokenRequest.java | 193 +++++++++++++++
.../dto/BackchannelLogoutTokenResponse.java | 230 ++++++++++++++++++
.../java/com/authlete/common/dto/Client.java | 142 ++++++++++-
.../java/com/authlete/common/dto/Service.java | 181 +++++++++++++-
10 files changed, 916 insertions(+), 19 deletions(-)
create mode 100644 src/main/java/com/authlete/common/dto/BackchannelLogoutTokenRequest.java
create mode 100644 src/main/java/com/authlete/common/dto/BackchannelLogoutTokenResponse.java
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index 1616355f4..e04a6a631 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,27 @@
変更点
======
+- `AuthleteApi` インターフェース
+ * `backchannelLogoutToken(BackchannelLogoutTokenRequest)` メソッドを追加。
+ * `backchannelLogoutToken(BackchannelLogoutTokenRequest, Options)` メソッドを追加。
+
+- `Client` クラス
+ * `getBackchannelLogoutUri()` メソッドを追加。
+ * `setBackchannelLogoutUri(URI)` メソッドを追加。
+ * `isBackchannelLogoutSessionRequired()` メソッドを追加。
+ * `setBackchannelLogoutSessionRequired(boolean)` メソッドを追加。
+
+- `Service` クラス
+ * `isBackchannelLogoutSupported()` メソッドを追加。
+ * `setBackchannelLogoutSupported(boolean)` メソッドを追加。
+ * `isBackchannelLogoutSessionSupported()` メソッドを追加。
+ * `setBackchannelLogoutSessionSupported(boolean)` メソッドを追加。
+
+- 新しい型
+ * `BackchannelLogoutTokenRequest` クラス
+ * `BackchannelLogoutTokenResponse` クラス
+
+
4.42 (2026 年 04 月 14 日)
--------------------------
diff --git a/CHANGES.md b/CHANGES.md
index 241ae1464..0b8533dc5 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,27 @@
CHANGES
=======
+- `AuthleteApi` interface
+ * Added the `backchannelLogoutToken(BackchannelLogoutTokenRequest)` method.
+ * Added the `backchannelLogoutToken(BackchannelLogoutTokenRequest, Options)` method.
+
+- `Client` class
+ * Added the `getBackchannelLogoutUri()` method.
+ * Added the `setBackchannelLogoutUri(URI)` method.
+ * Added the `isBackchannelLogoutSessionRequired()` method.
+ * Added the `setBackchannelLogoutSessionRequired(boolean)` method.
+
+- `Service` class
+ * Added the `isBackchannelLogoutSupported()` method.
+ * Added the `setBackchannelLogoutSupported(boolean)` method.
+ * Added the `isBackchannelLogoutSessionSupported()` method.
+ * Added the `setBackchannelLogoutSessionSupported(boolean)` method.
+
+- New types
+ * `BackchannelLogoutTokenRequest` class
+ * `BackchannelLogoutTokenResponse` class
+
+
4.42 (2026-04-14)
-----------------
diff --git a/src/main/java/com/authlete/common/api/AuthleteApi.java b/src/main/java/com/authlete/common/api/AuthleteApi.java
index 6efee6b01..d91a28fd0 100644
--- a/src/main/java/com/authlete/common/api/AuthleteApi.java
+++ b/src/main/java/com/authlete/common/api/AuthleteApi.java
@@ -38,6 +38,8 @@
import com.authlete.common.dto.BackchannelAuthenticationIssueResponse;
import com.authlete.common.dto.BackchannelAuthenticationRequest;
import com.authlete.common.dto.BackchannelAuthenticationResponse;
+import com.authlete.common.dto.BackchannelLogoutTokenRequest;
+import com.authlete.common.dto.BackchannelLogoutTokenResponse;
import com.authlete.common.dto.Client;
import com.authlete.common.dto.ClientAuthorizationGetListRequest;
import com.authlete.common.dto.ClientAuthorizationUpdateRequest;
@@ -4882,4 +4884,42 @@ default AttestationChallengeResponse attestationChallenge(
*/
AttestationChallengeResponse attestationChallenge(
AttestationChallengeRequest request, Options options) throws AuthleteApiException;
+
+
+ /**
+ * Call Authlete's {@code /backchannel/logout/token} API.
+ *
+ * @param request
+ * Request parameters passed to the API.
+ *
+ * @return
+ * Response from the API.
+ *
+ * @since 4.43
+ * @since Authlete 3.0.32
+ */
+ default BackchannelLogoutTokenResponse backchannelLogoutToken(
+ BackchannelLogoutTokenRequest request) throws AuthleteApiException
+ {
+ return backchannelLogoutToken(request, null);
+ }
+
+
+ /**
+ * Call Authlete's {@code /backchannel/logout/token} API.
+ *
+ * @param request
+ * Request parameters passed to the API.
+ *
+ * @param options
+ * Request options.
+ *
+ * @return
+ * Response from the API.
+ *
+ * @since 4.43
+ * @since Authlete 3.0.32
+ */
+ BackchannelLogoutTokenResponse backchannelLogoutToken(
+ BackchannelLogoutTokenRequest request, Options options) throws AuthleteApiException;
}
diff --git a/src/main/java/com/authlete/common/api/AuthleteApiImpl.java b/src/main/java/com/authlete/common/api/AuthleteApiImpl.java
index 828efcb97..71c60a878 100644
--- a/src/main/java/com/authlete/common/api/AuthleteApiImpl.java
+++ b/src/main/java/com/authlete/common/api/AuthleteApiImpl.java
@@ -43,6 +43,8 @@
import com.authlete.common.dto.BackchannelAuthenticationIssueResponse;
import com.authlete.common.dto.BackchannelAuthenticationRequest;
import com.authlete.common.dto.BackchannelAuthenticationResponse;
+import com.authlete.common.dto.BackchannelLogoutTokenRequest;
+import com.authlete.common.dto.BackchannelLogoutTokenResponse;
import com.authlete.common.dto.Client;
import com.authlete.common.dto.ClientAuthorizationDeleteRequest;
import com.authlete.common.dto.ClientAuthorizationGetListRequest;
@@ -195,6 +197,7 @@ class AuthleteApiImpl extends AuthleteApiBasicImpl
private static final String BC_AUTHENTICATION_COMPLETE_API_PATH = "/api/backchannel/authentication/complete";
private static final String BC_AUTHENTICATION_FAIL_API_PATH = "/api/backchannel/authentication/fail";
private static final String BC_AUTHENTICATION_ISSUE_API_PATH = "/api/backchannel/authentication/issue";
+ private static final String BC_LOGOUT_TOKEN_API_PATH = "/api/backchannel/logout/token";
private static final String DEVICE_AUTHORIZATION_API_PATH = "/api/device/authorization";
private static final String DEVICE_COMPLETE_API_PATH = "/api/device/complete";
private static final String DEVICE_VERIFICATION_API_PATH = "/api/device/verification";
@@ -1828,4 +1831,19 @@ public AttestationChallengeResponse attestationChallenge(
ATTESTATION_CHALLENGE_API_PATH)
.setOptions(options));
}
+
+
+ @Override
+ public BackchannelLogoutTokenResponse backchannelLogoutToken(
+ BackchannelLogoutTokenRequest request, Options options) throws AuthleteApiException
+ {
+ // Note that the /backchannel/logout/token API is not available in
+ // Authlete 2.x, so the executeApiCall below will throw an exception.
+
+ return executeApiCall(
+ new ServicePostApiCaller(
+ BackchannelLogoutTokenResponse.class, request,
+ BC_LOGOUT_TOKEN_API_PATH)
+ .setOptions(options));
+ }
}
diff --git a/src/main/java/com/authlete/common/api/AuthleteApiImplV3.java b/src/main/java/com/authlete/common/api/AuthleteApiImplV3.java
index 99866e261..fbf9857b6 100644
--- a/src/main/java/com/authlete/common/api/AuthleteApiImplV3.java
+++ b/src/main/java/com/authlete/common/api/AuthleteApiImplV3.java
@@ -44,6 +44,8 @@
import com.authlete.common.dto.BackchannelAuthenticationIssueResponse;
import com.authlete.common.dto.BackchannelAuthenticationRequest;
import com.authlete.common.dto.BackchannelAuthenticationResponse;
+import com.authlete.common.dto.BackchannelLogoutTokenRequest;
+import com.authlete.common.dto.BackchannelLogoutTokenResponse;
import com.authlete.common.dto.Client;
import com.authlete.common.dto.ClientAuthorizationDeleteRequest;
import com.authlete.common.dto.ClientAuthorizationGetListRequest;
@@ -196,6 +198,7 @@ public class AuthleteApiImplV3 extends AuthleteApiBasicImpl
private static final String BACKCHANNEL_AUTHENTICATION_COMPLETE_API_PATH = "/api/%d/backchannel/authentication/complete";
private static final String BACKCHANNEL_AUTHENTICATION_FAIL_API_PATH = "/api/%d/backchannel/authentication/fail";
private static final String BACKCHANNEL_AUTHENTICATION_ISSUE_API_PATH = "/api/%d/backchannel/authentication/issue";
+ private static final String BACKCHANNEL_LOGOUT_TOKEN_API_PATH = "/api/%d/backchannel/logout/token";
private static final String DEVICE_AUTHORIZATION_API_PATH = "/api/%d/device/authorization";
private static final String DEVICE_COMPLETE_API_PATH = "/api/%d/device/complete";
private static final String DEVICE_VERIFICATION_API_PATH = "/api/%d/device/verification";
@@ -1682,4 +1685,16 @@ public AttestationChallengeResponse attestationChallenge(
ATTESTATION_CHALLENGE_API_PATH, mServiceId)
.setOptions(options));
}
+
+
+ @Override
+ public BackchannelLogoutTokenResponse backchannelLogoutToken(
+ BackchannelLogoutTokenRequest request, Options options) throws AuthleteApiException
+ {
+ return executeApiCall(
+ new PostApiCaller(
+ BackchannelLogoutTokenResponse.class, request,
+ BACKCHANNEL_LOGOUT_TOKEN_API_PATH, mServiceId)
+ .setOptions(options));
+ }
}
diff --git a/src/main/java/com/authlete/common/dto/AuthorizationIssueRequest.java b/src/main/java/com/authlete/common/dto/AuthorizationIssueRequest.java
index 2a298dfc9..24ec702ad 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_logout_session_supported 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_logout_session_supported server
+ * metadata parameter is set to {@code true}.
+ *
+ *
* @return
* The session ID of the user's authentication session.
*
@@ -1683,6 +1701,10 @@ public AuthorizationIssueRequest setAccessTokenDuration(long duration)
* >OpenID Connect Native SSO for Mobile Apps 1.0
* @see AuthorizationResponse#isNativeSsoRequested()
* @see Service#isNativeSsoSupported()
+ *
+ * @see OpenID Connect Back-Channel Logout 1.0
+ * @see Service#isBackchannelLogoutSessionSupported()
*/
public String getSessionId()
{
@@ -1696,7 +1718,7 @@ public String getSessionId()
* 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,
@@ -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_logout_session_supported 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_logout_session_supported server
+ * metadata parameter is set to {@code true}.
+ *
+ *
* @param sessionId
* The session ID of the user's authentication session.
*
@@ -1725,6 +1765,10 @@ public String getSessionId()
* >OpenID Connect Native SSO for Mobile Apps 1.0
* @see AuthorizationResponse#isNativeSsoRequested()
* @see Service#isNativeSsoSupported()
+ *
+ * @see OpenID Connect Back-Channel Logout 1.0
+ * @see Service#isBackchannelLogoutSessionSupported()
*/
public AuthorizationIssueRequest setSessionId(String sessionId)
{
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 000000000..13f9caa87
--- /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_logout_session_supported
+ * server metadata parameter of your server is {@code true} (= if the
+ * backchannelLogoutSessionSupported property
+ * of your {@link Service} is {@code true}), this request parameter should
+ * be included.
+ *
+ *
+ * @return
+ * The session ID.
+ */
+ public String getSessionId()
+ {
+ return sessionId;
+ }
+
+
+ /**
+ * Set 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_logout_session_supported
+ * server metadata parameter of your server is {@code true} (= if the
+ * backchannelLogoutSessionSupported property
+ * of your {@link Service} is {@code true}), this request parameter should
+ * be included.
+ *
+ *
+ * @param sessionId
+ * The session ID.
+ *
+ * @return
+ * {@code this} object.
+ */
+ public BackchannelLogoutTokenRequest setSessionId(String sessionId)
+ {
+ this.sessionId = sessionId;
+
+ return this;
+ }
+}
diff --git a/src/main/java/com/authlete/common/dto/BackchannelLogoutTokenResponse.java b/src/main/java/com/authlete/common/dto/BackchannelLogoutTokenResponse.java
new file mode 100644
index 000000000..ae9532492
--- /dev/null
+++ b/src/main/java/com/authlete/common/dto/BackchannelLogoutTokenResponse.java
@@ -0,0 +1,230 @@
+/*
+ * 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.net.URI;
+
+
+/**
+ * Response from 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.
+ *
+ *
+ *
+ * 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 backchannelLogoutUri parameter
+ * contains the client's Back-Channel Logout URI, which corresponds to the
+ * backchannel_logout_uri 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_logout_uri metadata parameter is
+ * configured properly. Therefore, the value of the
+ * backchannelLogoutUri 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/Client.java b/src/main/java/com/authlete/common/dto/Client.java
index 5d372b2fe..a51deed71 100644
--- a/src/main/java/com/authlete/common/dto/Client.java
+++ b/src/main/java/com/authlete/common/dto/Client.java
@@ -84,10 +84,13 @@
*
* @see OAuth Client ID Metadata Document
+ *
+ * @see OpenID Connect Back-Channel Logout 1.0
*/
public class Client implements Serializable
{
- private static final long serialVersionUID = 41L;
+ private static final long serialVersionUID = 42L;
/*
@@ -738,6 +741,25 @@ public class Client implements Serializable
private String haipVersion;
+ /**
+ * The Back-Channel Logout URI.
+ *
+ * @since 4.43
+ * @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.43
+ * @since Authlete 3.0.32
+ */
+ private boolean backchannelLogoutSessionRequired;
+
+
/**
* Get the client number.
*
@@ -5841,7 +5863,7 @@ public Client setSpiffeBundleEndpoint(URI endpoint)
* The HAIP version to comply with.
*
* @since 4.41
- * @since 3.0.29
+ * @since Authlete 3.0.29
*
* @see
* OpenID4VC High Assurance Interoperability Profile 1.0
@@ -5881,7 +5903,7 @@ public String getHaipVersion()
* {@code this} object.
*
* @since 4.41
- * @since 3.0.29
+ * @since Authlete 3.0.29
*
* @see
* OpenID4VC High Assurance Interoperability Profile 1.0
@@ -5894,6 +5916,110 @@ public Client setHaipVersion(String version)
}
+ /**
+ * 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.43
+ * @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.43
+ * @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 Token s 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.43
+ * @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 Token s 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.43
+ * @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.
@@ -6197,6 +6323,16 @@ public Map toStandardMetadata(ClientMetadataControl control)
// spiffe_bundle_endpoint
put(metadata, "spiffe_bundle_endpoint", getSpiffeBundleEndpoint(), nullIncluded);
+ //----------------------------------------------------------------------
+ // OpenID Connect Back-Channel Logout 1.0
+ //----------------------------------------------------------------------
+
+ // backchannel_logout_uri
+ put(metadata, "backchannel_logout_uri", getBackchannelLogoutUri(), nullIncluded);
+
+ // backchannel_logout_session_required
+ put(metadata, "backchannel_logout_session_required", isBackchannelLogoutSessionRequired(), falseIncluded);
+
//----------------------------------------------------------------------
// Custom Metadata
//----------------------------------------------------------------------
diff --git a/src/main/java/com/authlete/common/dto/Service.java b/src/main/java/com/authlete/common/dto/Service.java
index 543136484..75cf8b258 100644
--- a/src/main/java/com/authlete/common/dto/Service.java
+++ b/src/main/java/com/authlete/common/dto/Service.java
@@ -330,7 +330,7 @@
*/
public class Service implements Serializable
{
- private static final long serialVersionUID = 92L;
+ private static final long serialVersionUID = 93L;
/*
@@ -2084,6 +2084,25 @@ public class Service implements Serializable
private boolean keyAttesterRootsOnly;
+ /**
+ * Whether to support the OpenID Connect Back-Channel Logout 1.0
+ * specification.
+ *
+ * @since 4.43
+ * @since Authlete 3.0.32
+ */
+ private boolean backchannelLogoutSupported;
+
+
+ /**
+ * Whether to support the {@code sid} claim in Logout Tokens.
+ *
+ * @since 4.43
+ * @since Authlete 3.0.32
+ */
+ private boolean backchannelLogoutSessionSupported;
+
+
/**
* Get the service number.
*
@@ -13786,4 +13805,164 @@ public Service setKeyAttesterRootsOnly(boolean only)
return this;
}
+
+
+ /**
+ * Get the flag indicating whether to support the OpenID
+ * Connect Back-Channel Logout 1.0 specification. This property
+ * corresponds to the backchannel_logout_supported
+ * server metadata parameter defined in the specification.
+ *
+ *
+ * The /api/{service-id} /backchannel/logout/token
+ * API can be used to generate a Logout Token . See {@link BackchannelLogoutTokenRequest} and
+ * {@link BackchannelLogoutTokenResponse} for details.
+ *
+ *
+ * @return
+ * The flag indicating whether to support the OpenID Connect Back-Channel Logout 1.0 specification.
+ *
+ * @since 4.43
+ * @since Authlete 3.0.32
+ *
+ * @see
+ * OpenID Connect Back-Channel Logout 1.0
+ * @see BackchannelLogoutTokenRequest
+ * @see BackchannelLogoutTokenResponse
+ */
+ public boolean isBackchannelLogoutSupported()
+ {
+ return backchannelLogoutSupported;
+ }
+
+
+ /**
+ * Set the flag indicating whether to support the OpenID
+ * Connect Back-Channel Logout 1.0 specification. This property
+ * corresponds to the backchannel_logout_supported
+ * server metadata parameter defined in the specification.
+ *
+ *
+ * The /api/{service-id} /backchannel/logout/token
+ * API can be used to generate a Logout Token . See {@link BackchannelLogoutTokenRequest} and
+ * {@link BackchannelLogoutTokenResponse} for details.
+ *
+ *
+ * @param supported
+ * {@code true} to indicate that this service supports the OpenID Connect Back-Channel Logout 1.0 specification.
+ *
+ * @return
+ * {@code this} object.
+ *
+ * @since 4.43
+ * @since Authlete 3.0.32
+ *
+ * @see
+ * OpenID Connect Back-Channel Logout 1.0
+ * @see BackchannelLogoutTokenRequest
+ * @see BackchannelLogoutTokenResponse
+ */
+ public Service setBackchannelLogoutSupported(boolean supported)
+ {
+ this.backchannelLogoutSupported = supported;
+
+ return this;
+ }
+
+
+ /**
+ * Get the flag indicating whether to support the {@code sid} claim of
+ * Logout Token s. This property corresponds to the
+ * 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/authorization/issue 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} /backchannel/logout/token
+ * API should include the {@code sessionId} request parameter (see
+ * {@link BackchannelLogoutTokenRequest#getSessionId()}).
+ *
+ *
+ * @return
+ * The flag indicating whether to support the {@code sid} claim
+ * of Logout Tokens.
+ *
+ * @since 4.43
+ * @since Authlete 3.0.32
+ *
+ * @see
+ * OpenID Connect Back-Channel Logout 1.0
+ */
+ public boolean isBackchannelLogoutSessionSupported()
+ {
+ return backchannelLogoutSessionSupported;
+ }
+
+
+ /**
+ * Set the flag indicating whether to support the {@code sid} claim of
+ * Logout Token s. This property corresponds to the
+ * 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/authorization/issue 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} /backchannel/logout/token
+ * API should include the {@code sessionId} request parameter (see
+ * {@link BackchannelLogoutTokenRequest#getSessionId()}).
+ *
+ *
+ * @param supported
+ * {@code true} to indicate that the {@code sid} claim of Logout
+ * Tokens is supported.
+ *
+ * @return
+ * {@code this} object.
+ *
+ * @since 4.43
+ * @since Authlete 3.0.32
+ *
+ * @see
+ * OpenID Connect Back-Channel Logout 1.0
+ */
+ public Service setBackchannelLogoutSessionSupported(boolean supported)
+ {
+ this.backchannelLogoutSessionSupported = supported;
+
+ return this;
+ }
}
From 37d068b5327afdc339b9002816a2595d78b07959 Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Sun, 10 May 2026 14:59:33 +0900
Subject: [PATCH 110/132] Updated documents for version 4.43.
---
CHANGES.ja.md | 3 +++
CHANGES.md | 3 +++
README.ja.md | 4 ++++
README.md | 4 ++++
4 files changed, 14 insertions(+)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index e04a6a631..6e82f7558 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,9 @@
変更点
======
+4.43 (2026 年 05 月 10 日)
+--------------------------
+
- `AuthleteApi` インターフェース
* `backchannelLogoutToken(BackchannelLogoutTokenRequest)` メソッドを追加。
* `backchannelLogoutToken(BackchannelLogoutTokenRequest, Options)` メソッドを追加。
diff --git a/CHANGES.md b/CHANGES.md
index 0b8533dc5..31e260324 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,9 @@
CHANGES
=======
+4.43 (2026-05-10)
+-----------------
+
- `AuthleteApi` interface
* Added the `backchannelLogoutToken(BackchannelLogoutTokenRequest)` method.
* Added the `backchannelLogoutToken(BackchannelLogoutTokenRequest, Options)` method.
diff --git a/README.ja.md b/README.ja.md
index 4e3d3c287..05c14a7c1 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -373,6 +373,10 @@ settings.setReadTimeout(5000);
- `attestationChallenge(AttestationChallengeRequest)`
+ 24. OpenID Connect Back-Channel Logout 1.0 に関するメソッド群
+
+ - `backchannelLogoutToken(BackchannelLogoutTokenRequest)`
+
*例*
次のコードは既存のサービスのリストを取得する例です。
diff --git a/README.md b/README.md
index b795cfbde..79f92da73 100644
--- a/README.md
+++ b/README.md
@@ -384,6 +384,10 @@ Methods in `AuthleteApi` interface can be divided into some categories.
- `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
From 18e8374779d3cbf4cc4821bdd55332592454653b Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Sun, 10 May 2026 06:02:52 +0000
Subject: [PATCH 111/132] [maven-release-plugin] prepare release
authlete-java-common-4.44
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index f79582923..e523d4925 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.44-SNAPSHOT
+ 4.44
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.21
+ authlete-java-common-4.44
From d27432727637eb6adf466a63bd5b2d0b72bd5dd1 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Sun, 10 May 2026 06:02:55 +0000
Subject: [PATCH 112/132] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index e523d4925..8d5d243a7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.44
+ 4.45-SNAPSHOT
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.44
+ authlete-java-common-4.21
From 6a565dc1831cc1122360b4d2a204a06da30d0b5a Mon Sep 17 00:00:00 2001
From: Takahiko Kawasaki
Date: Sun, 10 May 2026 15:22:49 +0900
Subject: [PATCH 113/132] fix: Fixed inconsistencies in the documentation
caused by the CHANGES files not being updated during the version 4.43
release.
---
CHANGES.ja.md | 10 +++++++++-
CHANGES.md | 10 +++++++++-
.../java/com/authlete/common/api/AuthleteApi.java | 4 ++--
src/main/java/com/authlete/common/dto/Client.java | 12 ++++++------
src/main/java/com/authlete/common/dto/Service.java | 12 ++++++------
5 files changed, 32 insertions(+), 16 deletions(-)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index 6e82f7558..3818b1ae7 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,7 +1,7 @@
変更点
======
-4.43 (2026 年 05 月 10 日)
+4.44 (2026 年 05 月 10 日)
--------------------------
- `AuthleteApi` インターフェース
@@ -25,6 +25,14 @@
* `BackchannelLogoutTokenResponse` クラス
+4.43 (2026 年 04 月 17 日)
+--------------------------
+
+- `AccessRight` 列挙型
+ * `USE_INTROSPECTION` を追加。
+ * `VIEW_AUDIT_LOG` を追加。
+
+
4.42 (2026 年 04 月 14 日)
--------------------------
diff --git a/CHANGES.md b/CHANGES.md
index 31e260324..d9bd04ab3 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,7 +1,7 @@
CHANGES
=======
-4.43 (2026-05-10)
+4.44 (2026-05-10)
-----------------
- `AuthleteApi` interface
@@ -25,6 +25,14 @@ CHANGES
* `BackchannelLogoutTokenResponse` class
+4.43 (2026-04-17)
+-----------------
+
+- `AccessRight` enum
+ * Added `USE_INTROSPECTION`.
+ * Added `VIEW_AUDIT_LOG`.
+
+
4.42 (2026-04-14)
-----------------
diff --git a/src/main/java/com/authlete/common/api/AuthleteApi.java b/src/main/java/com/authlete/common/api/AuthleteApi.java
index d91a28fd0..d77cd7fcc 100644
--- a/src/main/java/com/authlete/common/api/AuthleteApi.java
+++ b/src/main/java/com/authlete/common/api/AuthleteApi.java
@@ -4895,7 +4895,7 @@ AttestationChallengeResponse attestationChallenge(
* @return
* Response from the API.
*
- * @since 4.43
+ * @since 4.44
* @since Authlete 3.0.32
*/
default BackchannelLogoutTokenResponse backchannelLogoutToken(
@@ -4917,7 +4917,7 @@ default BackchannelLogoutTokenResponse backchannelLogoutToken(
* @return
* Response from the API.
*
- * @since 4.43
+ * @since 4.44
* @since Authlete 3.0.32
*/
BackchannelLogoutTokenResponse backchannelLogoutToken(
diff --git a/src/main/java/com/authlete/common/dto/Client.java b/src/main/java/com/authlete/common/dto/Client.java
index a51deed71..7209308ea 100644
--- a/src/main/java/com/authlete/common/dto/Client.java
+++ b/src/main/java/com/authlete/common/dto/Client.java
@@ -744,7 +744,7 @@ public class Client implements Serializable
/**
* The Back-Channel Logout URI.
*
- * @since 4.43
+ * @since 4.44
* @since Authlete 3.0.32
*/
private URI backchannelLogoutUri;
@@ -754,7 +754,7 @@ public class Client implements Serializable
* The flag indicating whether this client requires Logout Tokens to
* include the {@code sid} claim.
*
- * @since 4.43
+ * @since 4.44
* @since Authlete 3.0.32
*/
private boolean backchannelLogoutSessionRequired;
@@ -5926,7 +5926,7 @@ public Client setHaipVersion(String version)
* @return
* The Back-Channel Logout URI.
*
- * @since 4.43
+ * @since 4.44
* @since Authlete 3.0.32
*
* @see
@@ -5951,7 +5951,7 @@ public URI getBackchannelLogoutUri()
* @return
* {@code this} object.
*
- * @since 4.43
+ * @since 4.44
* @since Authlete 3.0.32
*
* @see
@@ -5978,7 +5978,7 @@ public Client setBackchannelLogoutUri(URI uri)
* The flag indicating whether this client requires Logout Tokens
* to include the {@code sid} claim.
*
- * @since 4.43
+ * @since 4.44
* @since Authlete 3.0.32
*
* @see
@@ -6006,7 +6006,7 @@ public boolean isBackchannelLogoutSessionRequired()
* @return
* {@code this} object.
*
- * @since 4.43
+ * @since 4.44
* @since Authlete 3.0.32
*
* @see
diff --git a/src/main/java/com/authlete/common/dto/Service.java b/src/main/java/com/authlete/common/dto/Service.java
index 75cf8b258..5837ec016 100644
--- a/src/main/java/com/authlete/common/dto/Service.java
+++ b/src/main/java/com/authlete/common/dto/Service.java
@@ -2088,7 +2088,7 @@ public class Service implements Serializable
* Whether to support the OpenID Connect Back-Channel Logout 1.0
* specification.
*
- * @since 4.43
+ * @since 4.44
* @since Authlete 3.0.32
*/
private boolean backchannelLogoutSupported;
@@ -2097,7 +2097,7 @@ public class Service implements Serializable
/**
* Whether to support the {@code sid} claim in Logout Tokens.
*
- * @since 4.43
+ * @since 4.44
* @since Authlete 3.0.32
*/
private boolean backchannelLogoutSessionSupported;
@@ -13827,7 +13827,7 @@ public Service setKeyAttesterRootsOnly(boolean only)
* "https://openid.net/specs/openid-connect-backchannel-1_0.html"
* >OpenID Connect Back-Channel Logout 1.0 specification.
*
- * @since 4.43
+ * @since 4.44
* @since Authlete 3.0.32
*
* @see
@@ -13864,7 +13864,7 @@ public boolean isBackchannelLogoutSupported()
* @return
* {@code this} object.
*
- * @since 4.43
+ * @since 4.44
* @since Authlete 3.0.32
*
* @see
@@ -13909,7 +13909,7 @@ public Service setBackchannelLogoutSupported(boolean supported)
* The flag indicating whether to support the {@code sid} claim
* of Logout Tokens.
*
- * @since 4.43
+ * @since 4.44
* @since Authlete 3.0.32
*
* @see
@@ -13953,7 +13953,7 @@ public boolean isBackchannelLogoutSessionSupported()
* @return
* {@code this} object.
*
- * @since 4.43
+ * @since 4.44
* @since Authlete 3.0.32
*
* @see
From aa60921a1d8a7fb397f2b7aeb29e091da315910c Mon Sep 17 00:00:00 2001
From: Otso Nuortimo
Date: Mon, 18 May 2026 10:14:30 +0900
Subject: [PATCH 114/132] fix: AccessRight - MODIFY_SERVICE, CREATE_CLIENT,
ADMIN now include USE_INTROSPECTION
---
CHANGES.ja.md | 7 +
CHANGES.md | 7 +
.../com/authlete/common/api/AccessRight.java | 6 +-
.../authlete/common/api/AccessRightTest.java | 166 ++++++++++++++++++
4 files changed, 183 insertions(+), 3 deletions(-)
create mode 100644 src/test/java/com/authlete/common/api/AccessRightTest.java
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index 3818b1ae7..eafa5c428 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,13 @@
変更点
======
+4.45 (2026 年 05 月 18 日)
+--------------------------
+
+- `AccessRight` 列挙型
+ * `MODIFY_SERVICE`、`CREATE_CLIENT`、`ADMIN` が `USE_INTROSPECTION` を含むように修正。
+
+
4.44 (2026 年 05 月 10 日)
--------------------------
diff --git a/CHANGES.md b/CHANGES.md
index d9bd04ab3..f083fa094 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,13 @@
CHANGES
=======
+4.45 (2026-05-18)
+-----------------
+
+- `AccessRight` enum
+ * Fixed `MODIFY_SERVICE`, `CREATE_CLIENT`, and `ADMIN` to include `USE_INTROSPECTION`.
+
+
4.44 (2026-05-10)
-----------------
diff --git a/src/main/java/com/authlete/common/api/AccessRight.java b/src/main/java/com/authlete/common/api/AccessRight.java
index 46f23b54d..49441f104 100644
--- a/src/main/java/com/authlete/common/api/AccessRight.java
+++ b/src/main/java/com/authlete/common/api/AccessRight.java
@@ -54,9 +54,9 @@ public enum AccessRight
/** can use the non-destructive service API calls (auth endpoint, token endpoint, etc.) */
USE_SERVICE(false, true, USE_INTROSPECTION, VIEW_SERVICE, VIEW_CLIENT),
/** can create new clients on this service */
- CREATE_CLIENT(false, true, USE_SERVICE, VIEW_SERVICE, MODIFY_CLIENT, VIEW_CLIENT),
+ CREATE_CLIENT(false, true, USE_INTROSPECTION, USE_SERVICE, VIEW_SERVICE, MODIFY_CLIENT, VIEW_CLIENT),
/** can modify this service */
- MODIFY_SERVICE(false, true, USE_SERVICE, VIEW_SERVICE, CREATE_CLIENT, MODIFY_CLIENT, VIEW_CLIENT),
+ MODIFY_SERVICE(false, true, USE_INTROSPECTION, USE_SERVICE, VIEW_SERVICE, CREATE_CLIENT, MODIFY_CLIENT, VIEW_CLIENT),
/** can view default service parameters */
VIEW_DEFAULT_SERVICE(false, false),
/** can create additional services */
@@ -66,7 +66,7 @@ public enum AccessRight
/** can delete a specific service */
DELETE_SERVICE(false, true),
/** can call administrative functions on the Authlete server */
- ADMIN(false, false, VIEW_DEFAULT_SERVICE, CREATE_SERVICE, DELETE_SERVICE, USE_SERVICE, VIEW_SERVICE, MODIFY_SERVICE, CREATE_CLIENT, MODIFY_CLIENT, VIEW_CLIENT)
+ ADMIN(false, false, VIEW_DEFAULT_SERVICE, CREATE_SERVICE, DELETE_SERVICE, USE_INTROSPECTION, USE_SERVICE, VIEW_SERVICE, MODIFY_SERVICE, CREATE_CLIENT, MODIFY_CLIENT, VIEW_CLIENT)
;
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 000000000..5ef8a6161
--- /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));
+ }
+}
From 2a2d1dc74fd4c8a9432883868f097d56936ee1b2 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Mon, 18 May 2026 01:39:29 +0000
Subject: [PATCH 115/132] [maven-release-plugin] prepare release
authlete-java-common-4.45
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 8d5d243a7..e88dc6d40 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.45-SNAPSHOT
+ 4.45
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.21
+ authlete-java-common-4.45
From 01c15d597dc3cbc84c15a1ee866c72b5e0028694 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Mon, 18 May 2026 01:39:32 +0000
Subject: [PATCH 116/132] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index e88dc6d40..19dde2ac8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.45
+ 4.46-SNAPSHOT
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.45
+ authlete-java-common-4.21
From d09821f6b0b70105fcd382c797fec7dcd5699816 Mon Sep 17 00:00:00 2001
From: Kyle Gonzalez
Date: Wed, 3 Jun 2026 16:18:23 +0900
Subject: [PATCH 117/132] feat: Add INVALID_SCOPE auth failure reason
---
.../authlete/common/dto/AuthorizationFailRequest.java | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/src/main/java/com/authlete/common/dto/AuthorizationFailRequest.java b/src/main/java/com/authlete/common/dto/AuthorizationFailRequest.java
index 4a690ba4a..9083dc121 100644
--- a/src/main/java/com/authlete/common/dto/AuthorizationFailRequest.java
+++ b/src/main/java/com/authlete/common/dto/AuthorizationFailRequest.java
@@ -261,6 +261,17 @@ public enum Reason
* @since 2.62
*/
INVALID_TARGET,
+
+ /**
+ * The requested scope is invalid.
+ *
+ *
+ * Using this reason will result in {@code error=invalid_scope}.
+ *
+ *
+ * @since 3.0.33
+ */
+ INVALID_SCOPE,
}
From 7051fa9dde08b25884b25d028f2642b6668c7244 Mon Sep 17 00:00:00 2001
From: Kyle Gonzalez
Date: Fri, 5 Jun 2026 13:26:12 +0900
Subject: [PATCH 118/132] feat: Add UNAUTHORIZED_CLIENT and
TEMPORARILY_UNAVAILABLE auth failure reasons.
---
.../common/dto/AuthorizationFailRequest.java | 21 +++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/src/main/java/com/authlete/common/dto/AuthorizationFailRequest.java b/src/main/java/com/authlete/common/dto/AuthorizationFailRequest.java
index 9083dc121..1645428cb 100644
--- a/src/main/java/com/authlete/common/dto/AuthorizationFailRequest.java
+++ b/src/main/java/com/authlete/common/dto/AuthorizationFailRequest.java
@@ -272,6 +272,27 @@ public enum Reason
* @since 3.0.33
*/
INVALID_SCOPE,
+
+ /**
+ * The client is not authorized to perform the requested operation.
+ *
+ *
+ * Using this reason will result in {@code error=unauthorized_client}.
+ *
+ * @since 3.0.33
+ */
+ UNAUTHORIZED_CLIENT,
+
+ /**
+ * The requested operation is temporarily unavailable and cannot be performed at this time.
+ *
+ *
+ * Using this reason will result in {@code error=temporarily_unavailable}.
+ *
+ *
+ * @since 3.0.33
+ */
+ TEMPORARILY_UNAVAILABLE,
}
From 3b5c1a46909a34f9328154a4a6aea45f5499a426 Mon Sep 17 00:00:00 2001
From: Kyle Gonzalez
Date: Fri, 5 Jun 2026 13:30:12 +0900
Subject: [PATCH 119/132] chore: Update CHANGES.md
---
CHANGES.ja.md | 9 +++++++++
CHANGES.md | 9 +++++++++
2 files changed, 18 insertions(+)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index eafa5c428..fc1c127d7 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,15 @@
変更点
======
+4.46 ()
+--------------------------
+
+- `AuthorizationFailRequest.Reason` 列挙型
+ * `INVALID_SCOPE` を追加。
+ * `UNAUTHORIZED_CLIENT` を追加。
+ * `TEMPORARILY_UNAVAILABLE` を追加。
+
+
4.45 (2026 年 05 月 18 日)
--------------------------
diff --git a/CHANGES.md b/CHANGES.md
index f083fa094..6b984af65 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,15 @@
CHANGES
=======
+4.46 ()
+-----------------
+
+- `AuthorizationFailRequest.Reason` enum
+ * Added `INVALID_SCOPE`
+ * Added `UNAUTHORIZED_CLIENT`
+ * Added `TEMPORARILY_UNAVAILABLE`
+
+
4.45 (2026-05-18)
-----------------
From 479f7d92fe530cce569c28cedf7b85709a2ce242 Mon Sep 17 00:00:00 2001
From: Kyle Gonzalez
Date: Tue, 9 Jun 2026 12:49:52 +0900
Subject: [PATCH 120/132] feat: Add the following token failure reasons:
INVALID_CLIENT, INVALID_GRANT, UNAUTHORIZED_CLIENT, UNSUPPORTED_GRANT_TYPE,
INVALID_SCOPE
---
CHANGES.ja.md | 6 ++
CHANGES.md | 7 ++
.../common/dto/AuthorizationFailRequest.java | 14 ++--
.../authlete/common/dto/TokenFailRequest.java | 79 ++++++++++++++++---
4 files changed, 88 insertions(+), 18 deletions(-)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index fc1c127d7..26498cf1d 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -9,6 +9,12 @@
* `UNAUTHORIZED_CLIENT` を追加。
* `TEMPORARILY_UNAVAILABLE` を追加。
+- `TokenFailRequest.Reason` 列挙型
+ * `INVALID_CLIENT` を追加。
+ * `INVALID_GRANT` を追加。
+ * `UNAUTHORIZED_CLIENT` を追加。
+ * `UNSUPPORTED_GRANT_TYPE` を追加。
+ * `INVALID_SCOPE` を追加。
4.45 (2026 年 05 月 18 日)
--------------------------
diff --git a/CHANGES.md b/CHANGES.md
index 6b984af65..ae32d0a01 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -9,6 +9,13 @@ CHANGES
* Added `UNAUTHORIZED_CLIENT`
* Added `TEMPORARILY_UNAVAILABLE`
+- `TokenFailRequest.Reason` enum
+ * Added `INVALID_CLIENT`
+ * Added `INVALID_GRANT`
+ * Added `UNAUTHORIZED_CLIENT`
+ * Added `UNSUPPORTED_GRANT_TYPE`
+ * Added `INVALID_SCOPE`
+
4.45 (2026-05-18)
-----------------
diff --git a/src/main/java/com/authlete/common/dto/AuthorizationFailRequest.java b/src/main/java/com/authlete/common/dto/AuthorizationFailRequest.java
index 1645428cb..47b7ae202 100644
--- a/src/main/java/com/authlete/common/dto/AuthorizationFailRequest.java
+++ b/src/main/java/com/authlete/common/dto/AuthorizationFailRequest.java
@@ -263,34 +263,36 @@ public enum Reason
INVALID_TARGET,
/**
- * The requested scope is invalid.
+ * The requested scope is invalid, unknown, or malformed.
*
*
* Using this reason will result in {@code error=invalid_scope}.
*
*
- * @since 3.0.33
+ * @since 4.46
*/
INVALID_SCOPE,
/**
- * The client is not authorized to perform the requested operation.
+ * The client is not authorized to request an authorization code using this method.
*
*
* Using this reason will result in {@code error=unauthorized_client}.
*
- * @since 3.0.33
+ * @since 4.46
*/
UNAUTHORIZED_CLIENT,
/**
- * The requested operation is temporarily unavailable and cannot be performed at this time.
+ * 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 3.0.33
+ * @since 4.46
*/
TEMPORARILY_UNAVAILABLE,
}
diff --git a/src/main/java/com/authlete/common/dto/TokenFailRequest.java b/src/main/java/com/authlete/common/dto/TokenFailRequest.java
index a834f5198..597032a78 100644
--- a/src/main/java/com/authlete/common/dto/TokenFailRequest.java
+++ b/src/main/java/com/authlete/common/dto/TokenFailRequest.java
@@ -63,37 +63,92 @@ 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 provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is
+ * invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to
+ * another client.
+ *
+ *
+ * Using this reason will result in {@code "error":"invalid_grant"}.
+ *
+ *
+ * @since 4.46
+ */
+ INVALID_GRANT,
+
+ /**
+ * 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 authorization grant type is not supported by the authorization server.
+ *
+ *
+ * Using this reason will result in {@code "error":"unsupported_grant_type"}.
+ *
+ *
+ * @since 4.46
+ */
+ UNSUPPORTED_GRANT_TYPE,
+
+ /**
+ * 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,
}
From 0f0592ee5b7bed72ad0b9beab667af3ecef39764 Mon Sep 17 00:00:00 2001
From: Kyle Gonzalez
Date: Wed, 10 Jun 2026 11:53:03 +0900
Subject: [PATCH 121/132] feat: Remove grant error reasons for token failure
requests.
---
.../authlete/common/dto/TokenFailRequest.java | 24 -------------------
1 file changed, 24 deletions(-)
diff --git a/src/main/java/com/authlete/common/dto/TokenFailRequest.java b/src/main/java/com/authlete/common/dto/TokenFailRequest.java
index 597032a78..80ffb34a3 100644
--- a/src/main/java/com/authlete/common/dto/TokenFailRequest.java
+++ b/src/main/java/com/authlete/common/dto/TokenFailRequest.java
@@ -104,19 +104,6 @@ public enum Reason
*/
INVALID_CLIENT,
- /**
- * The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is
- * invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to
- * another client.
- *
- *
- * Using this reason will result in {@code "error":"invalid_grant"}.
- *
- *
- * @since 4.46
- */
- INVALID_GRANT,
-
/**
* The authenticated client is not authorized to use this authorization grant type.
*
@@ -128,17 +115,6 @@ public enum Reason
*/
UNAUTHORIZED_CLIENT,
- /**
- * The authorization grant type is not supported by the authorization server.
- *
- *
- * Using this reason will result in {@code "error":"unsupported_grant_type"}.
- *
- *
- * @since 4.46
- */
- UNSUPPORTED_GRANT_TYPE,
-
/**
* The requested scope is invalid, unknown, malformed, or exceeds the scope granted by the resource owner.
*
From ea5e31c39db4f055e2001ebcab198f9c87e1caf9 Mon Sep 17 00:00:00 2001
From: Kyle Gonzalez
Date: Mon, 15 Jun 2026 17:02:29 +0900
Subject: [PATCH 122/132] chore: Update changes md files and add release date
for version 4.46.
---
CHANGES.ja.md | 4 +---
CHANGES.md | 4 +---
2 files changed, 2 insertions(+), 6 deletions(-)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index 26498cf1d..2bd6141e7 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,7 +1,7 @@
変更点
======
-4.46 ()
+4.46 (2026 年 06 月 15 日)
--------------------------
- `AuthorizationFailRequest.Reason` 列挙型
@@ -11,9 +11,7 @@
- `TokenFailRequest.Reason` 列挙型
* `INVALID_CLIENT` を追加。
- * `INVALID_GRANT` を追加。
* `UNAUTHORIZED_CLIENT` を追加。
- * `UNSUPPORTED_GRANT_TYPE` を追加。
* `INVALID_SCOPE` を追加。
4.45 (2026 年 05 月 18 日)
diff --git a/CHANGES.md b/CHANGES.md
index ae32d0a01..a94847c9a 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,7 +1,7 @@
CHANGES
=======
-4.46 ()
+4.46 (2026-06-15)
-----------------
- `AuthorizationFailRequest.Reason` enum
@@ -11,9 +11,7 @@ CHANGES
- `TokenFailRequest.Reason` enum
* Added `INVALID_CLIENT`
- * Added `INVALID_GRANT`
* Added `UNAUTHORIZED_CLIENT`
- * Added `UNSUPPORTED_GRANT_TYPE`
* Added `INVALID_SCOPE`
From 11d58ed0cdb0198a7f2b209404b37fe257bb213e Mon Sep 17 00:00:00 2001
From: Kyle Gonzalez
Date: Mon, 15 Jun 2026 17:08:21 +0900
Subject: [PATCH 123/132] fix: Formatting.
---
CHANGES.ja.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index 2bd6141e7..a6f41dde8 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -14,6 +14,7 @@
* `UNAUTHORIZED_CLIENT` を追加。
* `INVALID_SCOPE` を追加。
+
4.45 (2026 年 05 月 18 日)
--------------------------
From 4984231233651ce87c2875bd4baa5a2547657315 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Mon, 15 Jun 2026 08:16:17 +0000
Subject: [PATCH 124/132] [maven-release-plugin] prepare release
authlete-java-common-4.46
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 19dde2ac8..23c78107c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.46-SNAPSHOT
+ 4.46
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.21
+ authlete-java-common-4.46
From 439ee13bd1d7c610273805a6f6bbea6fd3262018 Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Mon, 15 Jun 2026 08:16:19 +0000
Subject: [PATCH 125/132] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 23c78107c..86dab8d69 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.46
+ 4.47-SNAPSHOT
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.46
+ authlete-java-common-4.21
From 1c58092feb71bc7f2778a6a0c6c24dbe07e5460e Mon Sep 17 00:00:00 2001
From: fabiohidekihirose
Date: Fri, 19 Jun 2026 15:58:23 +0900
Subject: [PATCH 126/132] feat: add note to TokenRevokeRequest and
TokenRevokeResponse about 20-token-per-call limit
---
.../java/com/authlete/common/dto/TokenRevokeRequest.java | 8 ++++++++
.../java/com/authlete/common/dto/TokenRevokeResponse.java | 6 +++++-
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/src/main/java/com/authlete/common/dto/TokenRevokeRequest.java b/src/main/java/com/authlete/common/dto/TokenRevokeRequest.java
index a5ed98fde..5c4c4e1bc 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 `clientIdentifier` only, `clientIdentifier` + `subject`,
+ * or `subject` only deletes at most **20 tokens per request**
+ * (the default of `token.revoke.count.max` in `ServerConfiguration.java`). If the
+ * target has more than 20 tokens, the response `count` will be 20 and the remainder
+ * is left untouched. To fully wipe them, call the endpoint repeatedly until `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 3286520b2..9a4b9b4f8 100644
--- a/src/main/java/com/authlete/common/dto/TokenRevokeResponse.java
+++ b/src/main/java/com/authlete/common/dto/TokenRevokeResponse.java
@@ -26,7 +26,11 @@ public class TokenRevokeResponse extends ApiResponse
{
private static final long serialVersionUID = 1L;
-
+ /**
+ * If the
+ * target has more than 20 tokens, the response `count` will be 20 and the remainder
+ * is left untouched. To fully wipe them, call the endpoint repeatedly until `count` returns 0.
+ */
private int count;
From 67ac18768929afb6257380313eb96842a5ebe58d Mon Sep 17 00:00:00 2001
From: Kyle
Date: Thu, 9 Jul 2026 10:44:43 +0900
Subject: [PATCH 127/132] feat: Add ClientCursorListResponse model class (#170)
* feat: Add CursorResponse model class
* chore: Update changes.md files.
---------
Co-authored-by: Kyle Gonzalez
---
CHANGES.ja.md | 7 ++
CHANGES.md | 7 ++
.../authlete/common/dto/CursorResponse.java | 117 ++++++++++++++++++
3 files changed, 131 insertions(+)
create mode 100644 src/main/java/com/authlete/common/dto/CursorResponse.java
diff --git a/CHANGES.ja.md b/CHANGES.ja.md
index a6f41dde8..cc1bfc9e0 100644
--- a/CHANGES.ja.md
+++ b/CHANGES.ja.md
@@ -1,6 +1,13 @@
変更点
======
+4.47 (2026 年 07 月 09 日)
+-----------------
+
+- 新しい型
+ * `CursorResponse` クラス
+
+
4.46 (2026 年 06 月 15 日)
--------------------------
diff --git a/CHANGES.md b/CHANGES.md
index a94847c9a..3f05fed0b 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,13 @@
CHANGES
=======
+4.47 (2026-07-09)
+-----------------
+
+- New types
+ * `CursorResponse` class
+
+
4.46 (2026-06-15)
-----------------
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 000000000..6683c58c4
--- /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 implements Serializable
+{
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The total number of entities, in situations where this is expensive to calculate it can be omitted and set to {@code null}.
+ */
+ private Integer totalCount = null;
+
+ /**
+ * The requested max limit of entities to return in this payload.
+ */
+ private int limit;
+
+ /**
+ * The array of entities that have been returned.
+ */
+ private T[] entities;
+
+ /**
+ * The opaque cursor token which is required for the next list request. If there are no further entities to retrieve this property will be {@code null}.
+ */
+ private String nextCursor;
+
+
+ public CursorResponse()
+ {
+
+ }
+
+
+ public CursorResponse(T[] entities, int limit, String nextCursor)
+ {
+ this.entities = entities;
+ this.limit = limit;
+ this.nextCursor = nextCursor;
+ }
+
+
+ public CursorResponse(T[] entities, int limit, String nextCursor, Integer totalCount)
+ {
+ this(entities, limit, nextCursor);
+ this.totalCount = totalCount;
+ }
+
+
+ /**
+ * Get the list of entities that match the list conditions.
+ *
+ * @return Array of entities that match, otherwise an empty array
+ */
+ public T[] getEntities()
+ {
+ return entities;
+ }
+
+
+ /**
+ * Get the interpreted limit of entities that will be return from the single call.
+ *
+ * @return the maximum amount of entities that could be returned from this single call
+ */
+ public int getLimit()
+ {
+ return limit;
+ }
+
+
+ /**
+ * Get the total count of entities available from this endpoint given the provided conditions.
+ *
+ * @return the maximum available amount of entities that can be retrieved at this point in time
+ */
+ public Integer getTotalCount()
+ {
+ return totalCount;
+ }
+
+
+ /**
+ * The next {@code cursor} that should be provided to this endpoint to retrieve the next portion of entities.
+ * If {@code null} it means all entities have been iterated over already and there are no further entities to retrieve.
+ *
+ * @return the next cursor value to be used if more entities can be retrieved, otherwise {@code null}
+ */
+ public String getNextCursor()
+ {
+ return nextCursor;
+ }
+}
From 0461e5ead9540bcc8f569ccb0aa8bec1941100de Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Thu, 9 Jul 2026 01:49:51 +0000
Subject: [PATCH 128/132] [maven-release-plugin] prepare release
authlete-java-common-4.47
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 86dab8d69..eccfa1c16 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.47-SNAPSHOT
+ 4.47
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.21
+ authlete-java-common-4.47
From d8a9b571ddaaef1caa2cc716656645c229f5031d Mon Sep 17 00:00:00 2001
From: Authlete OPS
Date: Thu, 9 Jul 2026 01:49:54 +0000
Subject: [PATCH 129/132] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index eccfa1c16..d7d6f9670 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.authlete
authlete-java-common
- 4.47
+ 4.48-SNAPSHOT
jar
${project.groupId}:${project.artifactId}
@@ -39,7 +39,7 @@
${git.connection}
${git.connection}
${git.url}
- authlete-java-common-4.47
+ authlete-java-common-4.21
From 105656c5da159067c6eba293632821064a161bb5 Mon Sep 17 00:00:00 2001
From: fabiohidekihirose
Date: Mon, 27 Jul 2026 12:13:44 +0900
Subject: [PATCH 130/132] fix: change markdown sintax to javadoc tags
---
.../com/authlete/common/dto/TokenRevokeRequest.java | 10 +++++-----
.../com/authlete/common/dto/TokenRevokeResponse.java | 10 ++++++++--
2 files changed, 13 insertions(+), 7 deletions(-)
diff --git a/src/main/java/com/authlete/common/dto/TokenRevokeRequest.java b/src/main/java/com/authlete/common/dto/TokenRevokeRequest.java
index 5c4c4e1bc..2bf60293b 100644
--- a/src/main/java/com/authlete/common/dto/TokenRevokeRequest.java
+++ b/src/main/java/com/authlete/common/dto/TokenRevokeRequest.java
@@ -81,11 +81,11 @@
*
*
*
- * Bulk revocation with `clientIdentifier` only, `clientIdentifier` + `subject`,
- * or `subject` only deletes at most **20 tokens per request**
- * (the default of `token.revoke.count.max` in `ServerConfiguration.java`). If the
- * target has more than 20 tokens, the response `count` will be 20 and the remainder
- * is left untouched. To fully wipe them, call the endpoint repeatedly until `count` returns 0.
+ * Bulk revocation with {@code clientIdentifier} only, {@code clientIdentifier} + {@code subject},
+ * or {@code subject} only deletes at most {@code 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
diff --git a/src/main/java/com/authlete/common/dto/TokenRevokeResponse.java b/src/main/java/com/authlete/common/dto/TokenRevokeResponse.java
index 9a4b9b4f8..513aeae87 100644
--- a/src/main/java/com/authlete/common/dto/TokenRevokeResponse.java
+++ b/src/main/java/com/authlete/common/dto/TokenRevokeResponse.java
@@ -28,14 +28,17 @@ public class TokenRevokeResponse extends ApiResponse
/**
* If the
- * target has more than 20 tokens, the response `count` will be 20 and the remainder
- * is left untouched. To fully wipe them, call the endpoint repeatedly until `count` returns 0.
+ * 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.
*/
private int count;
/**
* Get the number of revoked tokens.
+ * 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.
*
* @return
* The number of revoked tokens.
@@ -48,6 +51,9 @@ public int getCount()
/**
* Set the number of revoked tokens.
+ * 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.
*
* @param count
* The number of revoked tokens.
From 60248581b55fbffd7ae21353a2c3481160821151 Mon Sep 17 00:00:00 2001
From: fabiohidekihirose
Date: Wed, 29 Jul 2026 12:09:22 +0900
Subject: [PATCH 131/132] fix: update count description in TokenRevokeResponse
---
.../common/dto/TokenRevokeResponse.java | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/src/main/java/com/authlete/common/dto/TokenRevokeResponse.java b/src/main/java/com/authlete/common/dto/TokenRevokeResponse.java
index 513aeae87..2b16591fb 100644
--- a/src/main/java/com/authlete/common/dto/TokenRevokeResponse.java
+++ b/src/main/java/com/authlete/common/dto/TokenRevokeResponse.java
@@ -27,18 +27,18 @@ public class TokenRevokeResponse extends ApiResponse
private static final long serialVersionUID = 1L;
/**
- * 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.
+ * 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.
- * 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.
+ * 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.
@@ -51,9 +51,9 @@ public int getCount()
/**
* Set the number of revoked tokens.
- * 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.
+ * 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.
From bb32b7b1322d53a20c475aa3d6107e7144821ace Mon Sep 17 00:00:00 2001
From: fabiohidekihirose
Date: Thu, 30 Jul 2026 12:46:48 +0900
Subject: [PATCH 132/132] fix: add tag for 20 tokens per request note
---
src/main/java/com/authlete/common/dto/TokenRevokeRequest.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/com/authlete/common/dto/TokenRevokeRequest.java b/src/main/java/com/authlete/common/dto/TokenRevokeRequest.java
index 2bf60293b..90303484b 100644
--- a/src/main/java/com/authlete/common/dto/TokenRevokeRequest.java
+++ b/src/main/java/com/authlete/common/dto/TokenRevokeRequest.java
@@ -82,7 +82,7 @@
*
*
* Bulk revocation with {@code clientIdentifier} only, {@code clientIdentifier} + {@code subject},
- * or {@code subject} only deletes at most {@code 20 tokens per request}
+ * 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.