Box の JWT によるサーバー認証を試してみる
まとめ記事ができました
このスクラップについて
先ほどまでクライアント資格情報によるサーバー認証を試していたのだが as-user ヘッダーを使ってユーザーの代理アクセスを行うには JWT によるサーバー認証が必要となる可能性が高いことがわかった。
このスクラップでは Box の JWT によるサーバー認証を手を動かして試してみると共に as-user ヘッダーを使ってユーザーの代理アクセスができるかどうかを検証しようと思う。
ユーザーの代理アクセスはクライアント資格情報によるサーバー認証でも可能なことが後からわかりました。
関連スクラップ
クライアント資格情報許可によるサーバー認証については下記のスクラップにまとめた。
Box の Business プランを契約する手続きについては下記のスクラップにまとめた。
Box の Individual プラン(無料)で試したことについては下記のスクラップにまとめた。
JWT 認証に関する公式ドキュメント
この図を見てて気になった
もしかしてアプリアクセスレベルの設定を変更すればクライアント資格情報許可でも as-user ヘッダーを使える?
できなかった
アプリアクセスレベルを変更して再承認したがやはりクライアント資格情報許可ではできなかった。
エラーメッセージが少し変化した。
The API returned an error code [403 | .018e3a24895df8d8bf031cbe6c730b149]
前は下記が含まれていた。
access_denied_insufficient_permissions - Access denied - insufficient permission
気が済んだ
さっそくサーバー認証(JWT)のカスタムアプリの作成から始めよう。
はじめての JWT カスタムアプリ作成
開発者コンソールのアプリページへ移動してアプリの新規作成ボタンを押す。
カスタムアプリを押す。
アプリ名(例:My First JWT App)を入力する。
目的は自動化にしておく。
認証方法がサーバー認証(JWT 使用)であることを確認してアプリの作成ボタンを押す。
無事にカスタムアプリが作成された。
公開/秘密キーペアの生成
公開キーの追加と管理セクションにある公開/秘密キーペアを生成ボタンを押す。
2 段階認証のコードを入力する。
再び公開/秘密キーペアを生成ボタンを押すと JSON 形式の構成ファイルのダウンロードが始まる。
構成ファイルの中身は下記のような感じ。
{
"boxAppSettings": {
"clientID": "vvvv",
"clientSecret": "wwww",
"appAuth": {
"publicKeyID": "xxxx",
"privateKey": "-----BEGIN ENCRYPTED PRIVATE KEY-----\nyyyy\n-----END ENCRYPTED PRIVATE KEY-----\n",
"passphrase": "zzzz"
}
},
"enterpriseID": "1234567890"
}
権限はそのままにしておく
ユーザーの代理アクセスをするには設定を変更する必要がある。
設定についてはソースコードを作成してから変更しようと思う。
カスタムアプリの承認
危ない危ない、忘れるところだった、ドキュメントを読んでいて気が付いた。
管理コンソールのカスタムアプリマネージャのページでアプリの追加ボタンを押す。
クライアント ID を入力して次へボタンを押す。
承認ボタンを押す。
承認が完了してアプリが追加された。
ようやくコーディング
ドキュメントはこちら。
GitHub ページの方が詳細でわかりやすい。
Reader reader = new FileReader("src/example/config/config.json");
BoxConfig boxConfig = BoxConfig.readFrom(reader);
BoxDeveloperEditionAPIConnection api = BoxDeveloperEditionAPIConnection.getAppEnterpriseConnection(boxConfig);
BoxDeveloperEditionAPIConnection というのが気になるけど今のところは気にしないでおこう。
コーディング
import com.box.sdk.BoxConfig
import com.box.sdk.BoxDeveloperEditionAPIConnection
import com.box.sdk.BoxFolder
import java.io.FileReader
fun main(args: Array<String>) {
val fileName = System.getenv("BOX_CONFIG_FILE")
val reader = FileReader(fileName)
val boxConfig = BoxConfig.readFrom(reader)
val api = BoxDeveloperEditionAPIConnection.getAppEnterpriseConnection(boxConfig)
val rootFolder = BoxFolder.getRootFolder(api)
for (info in rootFolder) {
println("%s".format(info.name))
}
}
構成ファイルへのパスは環境変数を使って与えるようにした。
エラーが発生
下記の例外が発生、少し長いが全文を記載しておく。
Exception in thread "main" com.box.sdk.BoxAPIException: Error parsing PKCS private key for Box Developer Edition.
at com.box.sdk.BoxDeveloperEditionAPIConnection.decryptPrivateKey(BoxDeveloperEditionAPIConnection.java:568)
at com.box.sdk.BoxDeveloperEditionAPIConnection.constructJWTAssertion(BoxDeveloperEditionAPIConnection.java:503)
at com.box.sdk.BoxDeveloperEditionAPIConnection.authenticate(BoxDeveloperEditionAPIConnection.java:327)
at com.box.sdk.BoxDeveloperEditionAPIConnection.getAppEnterpriseConnection(BoxDeveloperEditionAPIConnection.java:182)
at com.box.sdk.BoxDeveloperEditionAPIConnection.getAppEnterpriseConnection(BoxDeveloperEditionAPIConnection.java:216)
at MainKt.main(Main.kt:11)
Caused by: org.bouncycastle.pkcs.PKCSException: unable to read encrypted data: 1.2.840.113549.1.5.13 not available: Illegal key size
at org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo.decryptPrivateKeyInfo(Unknown Source)
at com.box.sdk.BoxDeveloperEditionAPIConnection.decryptPrivateKey(BoxDeveloperEditionAPIConnection.java:557)
... 5 more
Caused by: org.bouncycastle.operator.OperatorCreationException: 1.2.840.113549.1.5.13 not available: Illegal key size
at org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder$1.get(Unknown Source)
... 7 more
Caused by: java.security.InvalidKeyException: Illegal key size
at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1039)
at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1060)
at javax.crypto.Cipher.init(Cipher.java:1536)
at javax.crypto.Cipher.init(Cipher.java:1470)
... 8 more
根深そう
色々と調べているが問題が根深そう。
やはりクライアント資格情報許可から試して正解だった。
いきなり JWT から始めてこの結果だったら心が折れてしまいそうだ。
今日はここまで
次回は JWT 認証が成功しない原因を調べるところから始めよう。
今日はここから
JWT 認証が成功しない原因を突き止めたいが今日中には終わらないだろう。
対症療法
- Java のバージョンを変えてみる
- Kotlin ではなく Java で試してみる
Java のバージョンを変えてみる
既存 Kotlin プロジェクトで Java バージョンを 8 → 17 に変更した所、警告が続出したのでプロジェクト自体を作り直す。
main() 関数の実行
Java 1.8 の場合とは違って build.gradle.kts を変更しなくても main() 関数を実行できた。
コーディング
ソースコードを前のものと同じなのでコピー&ペーストする。
import com.box.sdk.BoxConfig
import com.box.sdk.BoxDeveloperEditionAPIConnection
import com.box.sdk.BoxFolder
import java.io.FileReader
fun main(args: Array<String>) {
val fileName = System.getenv("BOX_CONFIG_FILE")
val reader = FileReader(fileName)
val boxConfig = BoxConfig.readFrom(reader)
val api = BoxDeveloperEditionAPIConnection.getAppEnterpriseConnection(boxConfig)
val rootFolder = BoxFolder.getRootFolder(api)
for (info in rootFolder) {
println("%s".format(info.name))
}
}
実行
エラーメッセージが表示された。
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
よくわからないけど下記の記事などが参考になりそう。
本家の方が参考になりそう
エラーメッセージは表示されるが動いてはいるようだ
import com.box.sdk.BoxConfig
import com.box.sdk.BoxDeveloperEditionAPIConnection
import com.box.sdk.BoxFolder
import java.io.FileReader
fun main(args: Array<String>) {
val fileName = System.getenv("BOX_CONFIG_FILE")
val reader = FileReader(fileName)
val boxConfig = BoxConfig.readFrom(reader)
val api = BoxDeveloperEditionAPIConnection.getAppEnterpriseConnection(boxConfig)
val rootFolder = BoxFolder.getRootFolder(api)
for (info in rootFolder) {
println("%s".format(info.name))
}
println("Exit") // この行を追加しました
}
実行するとエラーメッセージの後に Exit と表示される。
GitHub に ログに関するドキュメントがあった
下記も参考になるかも。
依存関係の追加
GitHub ドキュメントの見よう見まねで依存関係を追加してみる。
dependencies {
testImplementation(kotlin("test"))
implementation("com.box:box-java-sdk:4.1.0")
implementation("org.slf4j:jul-to-slf4j:1.7.32")
implementation("org.slf4j:slf4j-api:1.7.32")
implementation("ch.qos.logback:logback-core:1.2.7")
implementation("ch.qos.logback:logback-classic:1.2.7")
}
この状態で実行
たくさんのログが表示されるようになった。
11:12:44.259 [main] DEBUG org.jose4j.jwa.AlgorithmFactoryFactory - Initializing jose4j (running with Java 17.0.6 from Homebrew at /usr/local/Cellar/openjdk@17/17.0.6/libexec/openjdk.jdk/Contents/Home with [SUN version 17, SunRsaSign version 17, SunEC version 17, SunJSSE version 17, SunJCE version 17, SunJGSS version 17, SunSASL version 17, XMLDSig version 17, SunPCSC version 17, JdkLDAP version 17, JdkSASL version 17, Apple version 17, SunPKCS11 version 17, BC version 1.57] security providers installed)...
11:12:44.264 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - org.jose4j.jws.UnsecuredNoneAlgorithm(none|null) registered for alg algorithm none
11:12:44.265 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - org.jose4j.jws.HmacUsingShaAlgorithm$HmacSha256(HS256|HmacSHA256) registered for alg algorithm HS256
11:12:44.266 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - org.jose4j.jws.HmacUsingShaAlgorithm$HmacSha384(HS384|HmacSHA384) registered for alg algorithm HS384
11:12:44.266 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - org.jose4j.jws.HmacUsingShaAlgorithm$HmacSha512(HS512|HmacSHA512) registered for alg algorithm HS512
11:12:44.268 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - org.jose4j.jws.EdDsaAlgorithm(EdDSA|EdDSA) registered for alg algorithm EdDSA
11:12:44.269 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - org.jose4j.jws.EcdsaUsingShaAlgorithm$EcdsaP256UsingSha256(ES256|SHA256withECDSA) registered for alg algorithm ES256
11:12:44.269 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - org.jose4j.jws.EcdsaUsingShaAlgorithm$EcdsaP384UsingSha384(ES384|SHA384withECDSA) registered for alg algorithm ES384
11:12:44.270 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - org.jose4j.jws.EcdsaUsingShaAlgorithm$EcdsaP521UsingSha512(ES512|SHA512withECDSA) registered for alg algorithm ES512
11:12:44.285 [main] DEBUG org.jose4j.jws.EcdsaUsingShaAlgorithm$EcdsaSECP256K1UsingSha256 - ES256K is not available due to org.jose4j.lang.JoseException: Problem creating signature.; caused by: java.security.SignatureException: Curve not supported: java.security.spec.ECParameterSpec@793be5ca
11:12:44.286 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - ES256K is unavailable so will not be registered for alg algorithms.
11:12:44.286 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - org.jose4j.jws.RsaUsingShaAlgorithm$RsaSha256(RS256|SHA256withRSA) registered for alg algorithm RS256
11:12:44.286 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - org.jose4j.jws.RsaUsingShaAlgorithm$RsaSha384(RS384|SHA384withRSA) registered for alg algorithm RS384
11:12:44.286 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - org.jose4j.jws.RsaUsingShaAlgorithm$RsaSha512(RS512|SHA512withRSA) registered for alg algorithm RS512
11:12:44.289 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - org.jose4j.jws.RsaUsingShaAlgorithm$RsaPssSha256(PS256|RSASSA-PSS) registered for alg algorithm PS256
11:12:44.291 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - org.jose4j.jws.RsaUsingShaAlgorithm$RsaPssSha384(PS384|RSASSA-PSS) registered for alg algorithm PS384
11:12:44.294 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->JsonWebSignatureAlgorithm - org.jose4j.jws.RsaUsingShaAlgorithm$RsaPssSha512(PS512|RSASSA-PSS) registered for alg algorithm PS512
11:12:44.294 [main] DEBUG org.jose4j.jwa.AlgorithmFactoryFactory - JWS signature algorithms: [none, HS256, HS384, HS512, EdDSA, ES256, ES384, ES512, RS256, RS384, RS512, PS256, PS384, PS512]
11:12:44.298 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - org.jose4j.jwe.RsaKeyManagementAlgorithm$Rsa1_5(RSA1_5|RSA/ECB/PKCS1Padding) registered for alg algorithm RSA1_5
11:12:44.298 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - org.jose4j.jwe.RsaKeyManagementAlgorithm$RsaOaep(RSA-OAEP|RSA/ECB/OAEPWithSHA-1AndMGF1Padding) registered for alg algorithm RSA-OAEP
11:12:44.302 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - org.jose4j.jwe.RsaKeyManagementAlgorithm$RsaOaep256(RSA-OAEP-256|RSA/ECB/OAEPWithSHA-256AndMGF1Padding) registered for alg algorithm RSA-OAEP-256
11:12:44.303 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - org.jose4j.jwe.DirectKeyManagementAlgorithm(dir|null) registered for alg algorithm dir
11:12:44.305 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - org.jose4j.jwe.AesKeyWrapManagementAlgorithm$Aes128(A128KW|AESWrap) registered for alg algorithm A128KW
11:12:44.305 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - org.jose4j.jwe.AesKeyWrapManagementAlgorithm$Aes192(A192KW|AESWrap) registered for alg algorithm A192KW
11:12:44.306 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - org.jose4j.jwe.AesKeyWrapManagementAlgorithm$Aes256(A256KW|AESWrap) registered for alg algorithm A256KW
11:12:44.312 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - org.jose4j.jwe.EcdhKeyAgreementAlgorithm(ECDH-ES|ECDH) registered for alg algorithm ECDH-ES
11:12:44.319 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - org.jose4j.jwe.EcdhKeyAgreementWithAesKeyWrapAlgorithm$EcdhKeyAgreementWithAes128KeyWrapAlgorithm(ECDH-ES+A128KW|N/A) registered for alg algorithm ECDH-ES+A128KW
11:12:44.325 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - org.jose4j.jwe.EcdhKeyAgreementWithAesKeyWrapAlgorithm$EcdhKeyAgreementWithAes192KeyWrapAlgorithm(ECDH-ES+A192KW|N/A) registered for alg algorithm ECDH-ES+A192KW
11:12:44.331 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - org.jose4j.jwe.EcdhKeyAgreementWithAesKeyWrapAlgorithm$EcdhKeyAgreementWithAes256KeyWrapAlgorithm(ECDH-ES+A256KW|N/A) registered for alg algorithm ECDH-ES+A256KW
11:12:44.336 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - org.jose4j.jwe.Pbes2HmacShaWithAesKeyWrapAlgorithm$HmacSha256Aes128(PBES2-HS256+A128KW|n/a) registered for alg algorithm PBES2-HS256+A128KW
11:12:44.337 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - org.jose4j.jwe.Pbes2HmacShaWithAesKeyWrapAlgorithm$HmacSha384Aes192(PBES2-HS384+A192KW|n/a) registered for alg algorithm PBES2-HS384+A192KW
11:12:44.337 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - org.jose4j.jwe.Pbes2HmacShaWithAesKeyWrapAlgorithm$HmacSha512Aes256(PBES2-HS512+A256KW|n/a) registered for alg algorithm PBES2-HS512+A256KW
11:12:44.341 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - org.jose4j.jwe.AesGcmKeyEncryptionAlgorithm$Aes128Gcm(A128GCMKW|AES/GCM/NoPadding) registered for alg algorithm A128GCMKW
11:12:44.341 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - org.jose4j.jwe.AesGcmKeyEncryptionAlgorithm$Aes192Gcm(A192GCMKW|AES/GCM/NoPadding) registered for alg algorithm A192GCMKW
11:12:44.342 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->KeyManagementAlgorithm - org.jose4j.jwe.AesGcmKeyEncryptionAlgorithm$Aes256Gcm(A256GCMKW|AES/GCM/NoPadding) registered for alg algorithm A256GCMKW
11:12:44.342 [main] DEBUG org.jose4j.jwa.AlgorithmFactoryFactory - JWE key management algorithms: [RSA1_5, RSA-OAEP, RSA-OAEP-256, dir, A128KW, A192KW, A256KW, ECDH-ES, ECDH-ES+A128KW, ECDH-ES+A192KW, ECDH-ES+A256KW, PBES2-HS256+A128KW, PBES2-HS384+A192KW, PBES2-HS512+A256KW, A128GCMKW, A192GCMKW, A256GCMKW]
11:12:44.343 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->ContentEncryptionAlgorithm - org.jose4j.jwe.AesCbcHmacSha2ContentEncryptionAlgorithm$Aes128CbcHmacSha256(A128CBC-HS256|AES/CBC/PKCS5Padding) registered for enc algorithm A128CBC-HS256
11:12:44.343 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->ContentEncryptionAlgorithm - org.jose4j.jwe.AesCbcHmacSha2ContentEncryptionAlgorithm$Aes192CbcHmacSha384(A192CBC-HS384|AES/CBC/PKCS5Padding) registered for enc algorithm A192CBC-HS384
11:12:44.344 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->ContentEncryptionAlgorithm - org.jose4j.jwe.AesCbcHmacSha2ContentEncryptionAlgorithm$Aes256CbcHmacSha512(A256CBC-HS512|AES/CBC/PKCS5Padding) registered for enc algorithm A256CBC-HS512
11:12:44.345 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->ContentEncryptionAlgorithm - org.jose4j.jwe.AesGcmContentEncryptionAlgorithm$Aes128Gcm(A128GCM|AES/GCM/NoPadding) registered for enc algorithm A128GCM
11:12:44.346 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->ContentEncryptionAlgorithm - org.jose4j.jwe.AesGcmContentEncryptionAlgorithm$Aes192Gcm(A192GCM|AES/GCM/NoPadding) registered for enc algorithm A192GCM
11:12:44.346 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->ContentEncryptionAlgorithm - org.jose4j.jwe.AesGcmContentEncryptionAlgorithm$Aes256Gcm(A256GCM|AES/GCM/NoPadding) registered for enc algorithm A256GCM
11:12:44.346 [main] DEBUG org.jose4j.jwa.AlgorithmFactoryFactory - JWE content encryption algorithms: [A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM]
11:12:44.347 [main] DEBUG org.jose4j.jwa.AlgorithmFactory->CompressionAlgorithm - org.jose4j.zip.DeflateRFC1951CompressionAlgorithm@d9345cd registered for zip algorithm DEF
11:12:44.347 [main] DEBUG org.jose4j.jwa.AlgorithmFactoryFactory - JWE compression algorithms: [DEF]
11:12:44.347 [main] DEBUG org.jose4j.jwa.AlgorithmFactoryFactory - Initialized jose4j in 85ms
Exit
ユーザーの代理アクセス
どうやら動いている様子なのでユーザーの代理アクセスを試してみる。
import com.box.sdk.BoxConfig
import com.box.sdk.BoxDeveloperEditionAPIConnection
import com.box.sdk.BoxFolder
import java.io.FileReader
fun main(args: Array<String>) {
val fileName = System.getenv("BOX_CONFIG_FILE")
val userID = System.getenv("BOX_USER_ID")
val reader = FileReader(fileName)
val boxConfig = BoxConfig.readFrom(reader)
val api = BoxDeveloperEditionAPIConnection.getAppEnterpriseConnection(boxConfig)
api.asUser(userID)
val rootFolder = BoxFolder.getRootFolder(api)
for (info in rootFolder) {
println("%s".format(info.name))
}
println("Exit")
}
この時点ではカスタムアプリの権限を適切に設定していないので期待通り失敗する。
Exception in thread "main" com.box.sdk.BoxAPIResponseException: The API returned an error code [403 | nsaffzhdcvsz2dkq.053c8d43eef5fb06b5e0cc1ee1700ddba] access_denied_insufficient_permissions - Access denied - insufficient permission
まずは as-user ヘッダーを有効にしてみる
再承認を忘れないようにする。
Exception in thread "main" com.box.sdk.BoxAPIResponseException: The API returned an error code [403 | e8xv8ihdcvyzesc1.08f162971b1dc1773618e2f21351b1ed4] access_denied_insufficient_permissions - Access denied - insufficient permission
この時点で実行しても結果は変わらなかった。
アプリアクセスレベルの変更
開発者コンソール
管理コンソール
エラーメッセージが変化した。
Exception in thread "main" com.box.sdk.BoxAPIResponseException: The API returned an error code [403 | .0884fb7f5eb8ab3aef87584aa570f2349]
ここまではクライアント資格情報許可と同様だ。
色々やってたらできた!
自分でもどうしてできたのかわからないので後からゆっくりまとめよう。
アプリアクセスレベルの変更には注意
変更すると連動してアプリケーションスコープの設定が勝手に上書きされる。
変更前
変更後
as-user の許可にはユーザー管理の権限が必須
as-user のチェックボックスにチェックを入れるとユーザー管理のチェックボックスにもチェックが入る。
as-user チェック前
as-user チェック後
ユーザー管理のチェックボックスからチェックを外すと as-user のチェックボックスからもチェックが外れる。
ユーザー管理からチェックを外した後
変更が反映されるまでに遅延がある
開発者コンソールでカスタムアプリの設定を変更した後に管理コンソールで承認するまでの時間が短すぎると反映がされていない。
例えば下記の画像ではアプリケーションアクセスがすべてのユーザーになっているが、本来はこのアプリの App User のみが正しい。
しばらく待つかカスタムアプリの詳細を表示したりすると反映される。
設定変更が反映されていることをしっかり確認してから再承認する必要がある。
この状態で実行する
下記の例外が発生する。
Exception in thread "main" com.box.sdk.BoxAPIResponseException: The API returned an error code [403 | sskifbhdd4mcmnsu.0a9c00049ce22e46e05d2a6cde9b94540] access_denied_insufficient_permissions - Access denied - insufficient permission
やはりアプリケーションアクセルレベルがアプリアクセスのみではダメなようだ。
アプリアクセスレベルを変更する
アプリ + Enterprise アクセスにする。
as-user ヘッダーの許可はしないでおく。
この状態で変更を保存してから管理コンソールでしっかり確認してから再承認する。
この状態で実行する
期待通り例外が発生した。
Exception in thread "main" com.box.sdk.BoxAPIResponseException: The API returned an error code [403 | .0a61d1efed33a1969d50dad23a6250e5c]
as-user を許可する
as-user にチェックを入れる、必然的にユーザーを管理するにもチェックが入る。
この状態で変更を保存してしっかり確認してから再承認する。
この状態で実行する
サービスアカウントではなくユーザー(僕)のルートフォルダーの一覧が表示された。
Box Reports
It works!
Exit
もしかしてクライアント資格情報許可でもできる?
同じ条件で試してみたところできてしまった。
start
[204837656150] Box Reports
[204677117397] It works!
end
前にやった時は設定が間違っていただけでクライアント資格情報許可でもユーザーの代理アクセスができることがわかった。
今日はここまで
ユーザーの代理アクセスを検証できて今日はキリの良いところで終わることができた。
Java 17 ではできたが Java 8 ではできていないのでせっかくなので次回は再挑戦してみたい。
今日はここから
今日は Java 8 で JWT 認証に再挑戦したい。
せっかくなので新しいスクラップを作ろう
まとめたらクローズする
今回のスクラップ作成を通じて学んだことをまとめたらこのスクラップをクローズしよう。
まとめ
- ユーザーの代理アクセスにはアプリアクセスレベルをアプリ + Enterprise アクセスに設定する必要がある。
- アプリ + Enterprise アクセスは再承認モーダルでは「すべてユーザー」と表示される。
- JWT 認証では公開キーを登録するか公開/秘密キーペアを生成する必要がある。
- キーペアを生成した場合は JSON 形式の構成ファイルがダウンロードされる。
- クライアント資格情報許可と同様、JWT 認証でも管理コンソールから承認が必要となる。
- Java 8 では構成ファイル読み込み時に PKCS 秘密鍵がパースできなくて例外が発生する場合がある。
- Java 17 では例外は発生しなかった。
- 構成ファイル読み込み時に SLF4J の警告が表示されるがプログラム自体は最後まで動く。
- SLF4J の警告はログ関係の依存関係 4 件を追加することで表示されなくなる。
- ユーザー代理アクセスには as-header を有効にする必要がある。
- アプリアクセスレベルを変更すると連動して他の設定が上書きされるので注意する。
- as-header を有効にするとユーザー管理も自動的に有効になる。
- ユーザー管理を無効にすると as-header も自動的に無効になる。
- 管理コンソールで承認する時に権限設定が反映されていないことがあるので注意する。
- 承認したカスタムアプリの詳細ページなどを表示することで権限設定が反映されることがある。
- ユーザーの代理アクセスは設定が正しければクライアント資格情報許可でも行える。
おわりに
ユーザー代理アクセス
ユーザーの代理アクセスが可能な点を検証できて良かった。
ユーザーの代理アクセスを使えばできることの幅が広がるので様々な要件に対応ができそう。
その一方でユーザーの代理アクセスはとても強力な権限なので適切に使う必要があると考えている。
代わりにコラボレーション(フォルダー共有)を使うこともできるので、要件に応じて適切に選択できるようになりたい。
ユーザー → サービスアカウントのコラボレーションは検証できたけど、逆方向のサービスアカウント → ユーザーのコラボレーションはできるのかな?
これは面白そうなので近日検証してみよう。
JWT 認証
Box 側が保持する情報が公開鍵だけなのでクライアント資格情報許可に比べて原理上は安全だが、結局はこちら側で秘密鍵を安全に管理しなければならないので、クライアントシークレットの場合とあまり変わらない印象を受ける。
誤解を恐れずに言えば共通鍵暗号と公開鍵暗号のどっちが安全かという議論に近い感じがする。
JWT とクライアント資格情報許可でできることはあまり変わらないみたいなので適切に鍵を管理できるのであればクライアント資格情報許可でも良い気がする。
JWT 認証は Java 8 だと Oracle でユーザー登録して JAR をダウンロードし、JAVA_HOME/lib/security に追加するという作業が必要になる。
開発環境ではこの作業は 1 回やれば良いので少し面倒なくらいで大したことはないが、運用環境では難しいケースもあるかも知れない。
まだまだ勉強不足で JWT のメリットをよく理解できてないだけかも知れないが、現時点では要件的に許されるのであればクライアント資格情報許可を選ぶことになりそう。
カスタムアプリの権限設定と承認
罠が多すぎるのでもう少し良い UI にして欲しいが Box の UI はかなり良い方だと思うので気を付けるしか無さそうだ。