メインコンテンツへスキップ
このガイドでは、Box SDKを使用しないJWT認証について説明します。JWTはエンドユーザーによる操作を必要とせず、Box APIで直接認証するよう設計されています。 このトークンの使用方法を確認するには、APIコールの実行に関するガイドを参照してください。
デフォルトでは、JWTを使用して取得したアクセストークンは、アプリケーションのサービスアカウントに関連付けられています。このトークンを使用して実行されるAPIコールはすべて、このアプリケーションから実行されます。アプリケーションのサービスアカウントがコラボレータとして追加されるまで、このアカウントでは、既存のファイルやフォルダにアクセスできません。as-userヘッダーを使用するかユーザーアクセストークンをリクエストして、別のユーザーとして処理を実行できます。

キーペアの使用

公開キーと秘密キーのペアを使用してアプリケーションのIDを確認する場合は、以下の手順に従います。

前提条件

  • 開発者コンソールでJWT認証を使用するPlatformアプリケーション
  • config.jsonという名前の秘密キー構成ファイル (開発者コンソールの [構成] タブからダウンロード可能)
  • Box管理コンソールでアプリケーションが承認されていること

1. JSON構成を読み取る

config.jsonファイルには、アプリケーションの秘密キーと、認証に必要なその他の詳細が含まれています。このファイルの例を以下に示します。
{
  "boxAppSettings": {
    "clientID": "abc...123",
    "clientSecret": "def...234",
    "appAuth": {
      "publicKeyID": "abcd1234",
      "privateKey": "-----BEGIN ENCRYPTED PRIVATE KEY-----\n....\n-----END ENCRYPTED PRIVATE KEY-----\n",
      "passphrase": "ghi...345"
    }
  },
  "enterpriseID": "1234567"
}

このオブジェクトをアプリケーションで使用するには、ファイルから読み取る必要があります。
using System;
using System.IO;
using Newtonsoft.Json;

class Config
{
    public class BoxAppSettings {
        public class AppAuth {
            public string privateKey { get; set; }
            public string passphrase { get; set; }
            public string publicKeyID { get; set; }
        }
        public string clientID { get; set; }
        public string clientSecret { get; set; }
        public AppAuth appAuth { get; set; }

    }
    public string enterpriseID { get; set; }
    public BoxAppSettings boxAppSettings { get; set; }
}

var reader = new StreamReader("config.json");
var json = reader.ReadToEnd();

var config = JsonConvert.DeserializeObject<Config>(json);

JSONの解析プログラミング言語によっては、ファイルからJSONを読み取って解析する方法が複数ある場合があります。エラー処理など、さらに詳細な説明については、使用するプログラミング言語のガイドを参照してください。

2. 秘密キーを復号化する

JWTアサーションを作成するために、アプリケーションでは構成オブジェクトにある秘密キーが必要になります。この秘密キーは暗号化されており、ロックを解除するにはパスコードが必要です。暗号化されたキーとパスコードは両方とも、構成オブジェクトで指定されています。
using System.Security.Cryptography;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;

// https://www.bouncycastle.org/csharp/index.html
class PasswordFinder : IPasswordFinder
{
    private string password;
    public PasswordFinder(string _password) { password = _password; }
    public char[] GetPassword() { return password.ToCharArray(); }
}

var appAuth = config.boxAppSettings.appAuth;
var stringReader = new StringReader(appAuth.privateKey);
var passwordFinder = new PasswordFinder(appAuth.passphrase);
var pemReader = new PemReader(stringReader, passwordFinder);
var keyParams = (RsaPrivateCrtKeyParameters) pemReader.ReadObject();

public RSA CreateRSAProvider(RSAParameters rp)
{
    var rsaCsp = RSA.Create();
    rsaCsp.ImportParameters(rp);
    return rsaCsp;
}

public RSAParameters ToRSAParameters(RsaPrivateCrtKeyParameters privKey)
{
    RSAParameters rp = new RSAParameters();
    rp.Modulus = privKey.Modulus.ToByteArrayUnsigned();
    rp.Exponent = privKey.PublicExponent.ToByteArrayUnsigned();
    rp.P = privKey.P.ToByteArrayUnsigned();
    rp.Q = privKey.Q.ToByteArrayUnsigned();
    rp.D = ConvertRSAParametersField(privKey.Exponent, rp.Modulus.Length);
    rp.DP = ConvertRSAParametersField(privKey.DP, rp.P.Length);
    rp.DQ = ConvertRSAParametersField(privKey.DQ, rp.Q.Length);
    rp.InverseQ = ConvertRSAParametersField(privKey.QInv, rp.Q.Length);
    return rp;
}

public byte[] ConvertRSAParametersField(BigInteger n, int size)
{
    byte[] bs = n.ToByteArrayUnsigned();
    if (bs.Length == size)
        return bs;
    if (bs.Length > size)
        throw new ArgumentException("Specified size too small", "size");
    byte[] padded = new byte[size];
    Array.Copy(bs, 0, padded, size - bs.Length, bs.Length);
    return padded;
}

var key = CreateRSAProvider(ToRSAParameters(keyParams));

ファイルから秘密キーを読み込むための代替方法アプリケーションでは、秘密キーとパスワードの両方をディスクに保存しておきたくない場合があります。代替方法として、パスワードを環境変数として渡し、秘密キーを、秘密キーのロックを解除するためのトークンと分けておくこともできます。

3. JWTアサーションを作成する

Box APIで認証するために、アプリケーションは、アクセストークンと交換できる署名済みのJWTアサーションを作成する必要があります。 JWTアサーションは、暗号化されたJSONオブジェクトで、headerclaims、およびsignatureで構成されます。最初にclaimsを作成します。これは、payloadとも呼ばれる場合もあります。
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Collections.Generic;

byte[] randomNumber = new byte[64];
RandomNumberGenerator.Create().GetBytes(randomNumber);
var jti = Convert.ToBase64String(randomNumber);

DateTime expirationTime = DateTime.UtcNow.AddSeconds(45);

var claims = new List<Claim>{
    new Claim("sub", config.enterpriseID),
    new Claim("box_sub_type", "enterprise"),
    new Claim("jti", jti),
};

パラメータ説明
iss (必須)StringBoxアプリケーションのOAuthクライアントID
sub (必須)StringBox Enterprise ID (このアプリがそのアプリケーションのサービスアカウントの代わりになる場合) またはユーザーID (このアプリが別のユーザーの代わりになる場合)。
box_sub_type (必須)Stringenterpriseまたはuser (subクレームでリクエストされているトークンの種類に応じて決定)
aud (必須)String常にhttps://api.box.com/oauth2/token
jti (必須)StringこのJWTに対してアプリケーションで指定されたUUID (Universally Unique Identifier)。16文字以上128文字以下の一意の文字列です。
exp (必須)IntegerこのJWTが期限切れとなるUnix時間。設定できる最大値は、発行時刻から60秒後です。許容される最大値よりも小さい値を設定することをお勧めします。
iat (省略可)Integer発行時刻。トークンは、この時刻より前に使用することはできません。
nbf (省略可)Integer開始時刻。トークンの有効期間の開始時刻を指定します。
次に、秘密キーを使用してこれらのクレームに署名する必要があります。使用する言語とライブラリに応じて、クレームの署名に使用する暗号化アルゴリズムと公開キーのIDを定義することで、JWTのheaderが構成されます。
using Microsoft.IdentityModel.Tokens;

String authenticationUrl = "https://api.box.com/oauth2/token";

var payload = new JwtPayload(
    config.boxAppSettings.clientID,
    authenticationUrl,
    claims,
    null,
    expirationTime
);

var credentials = new SigningCredentials(
    new RsaSecurityKey(key),
    SecurityAlgorithms.RsaSha512
);
var header = new JwtHeader(signingCredentials: credentials);

var jst = new JwtSecurityToken(header, payload);
var tokenHandler = new JwtSecurityTokenHandler();
string assertion = tokenHandler.WriteToken(jst);

ヘッダーでは、以下のパラメータがサポートされます。
パラメータ説明
algorithm (必須)StringJWTクレームへの署名に使用する暗号化アルゴリズム。RS256、RS384、RS512のいずれかを指定できます。
keyid (必須)StringJWTへの署名に使用する公開キーのID。必須ではありませんが、アプリケーションに対して複数のキーペアが定義される場合は必須です。
JWTライブラリの使用独自のJWTへの署名は、複雑で手間のかかる処理になる可能性があります。そのようなことがないよう、事前にこの処理を済ませたライブラリがほぼすべての言語で用意されています。概要については、JWT.ioをご覧ください。

4. アクセストークンをリクエストする

最後の手順として、有効期間の短いJWTアサーションを、より有効期間の長いアクセストークンと交換します。これには、アサーションをパラメータに指定してトークンエンドポイントを呼び出します。
using System.Net;
using System.Net.Http;

var content = new FormUrlEncodedContent(new[]
{
    new KeyValuePair<string, string>(
        "grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer"),
    new KeyValuePair<string, string>(
        "assertion", assertion),
    new KeyValuePair<string, string>(
        "client_id", config.boxAppSettings.clientID),
    new KeyValuePair<string, string>(
        "client_secret", config.boxAppSettings.clientSecret)
});

var client = new HttpClient();
var response = client.PostAsync(authenticationUrl, content).Result;

class Token
{
    public string access_token { get; set; }
}

var data = response.Content.ReadAsStringAsync().Result;
var token = JsonConvert.DeserializeObject<Token>(data);
var accessToken = token.access_token;

コードサンプル

このガイドに記載されているコードは、GitHubで入手できます。