Manual Token request, with public key

Hello,
I have been attempting to do a request to :
https://api.box.com/oauth2/token

My dev setup, is almost identical to my prod setup with one exception. In prod i use a Public Key. I am rewriting a tool i made using the NodeSDK in Rust. The issue I have is, in the NodeSDK you can pass it a thing like this:
{
“boxAppSettings”: {
“clientID”: “hvauxhjq98474895cej49uq2na1k4m3hca”,
“clientSecret”: “********************************”,
“appAuth”: {
“publicKeyID”: “”,
“privateKey”: “”,
“passphrase”: “”
}
},
“enterpriseID”: “42069”
}

Then i just pass it to my sdk as a preConfigured instance. HOWEVER, I have my Rust based Wrapper working with my dev account, but when I go to move into prod, I must be missing something, as i cant find examples of how to do this using a direct http request:

For example:

Where is the documentation on this process but with ‘curl’ or some other direct http calls?
I bet it exists but there seems to be no clear linking to it?

Any help and guidance on where to find this information would be greatly appreciated.
Thank you,
Andrew

Ok also something else super wierd while digging into this:
On the Left is my Prod console. On the Right is my Devconsole:


As I was comparing them to make sure I didnt miss anything, um, why is there different text in these boxes. They are configured exactly the same with the single exception being the PublicKey Pair.

Does this indicate something?!

Hi @AndrewC , welcome to the forum!

You can absolutely do a manual HTTP POST request to get a new access token.

The issue might be how to generate a JWT assertion that you must include in that request.

For example in our postman collection, we have this pre-request script for getting and access token using JWT:

async function refresh_jwt (assertion) {
  const urlencoded = [
    { key: 'client_id', value: pm.environment.get('client_id'), disabled: false },
    { key: 'client_secret', value: pm.environment.get('client_secret'), disabled: false },
    { key: 'box_subject_type', value: pm.environment.get('box_subject_type'), disabled: false },
    { key: 'box_subject_id', value: pm.environment.get('box_subject_id'), disabled: false },
    { key: 'grant_type', value: 'urn:ietf:params:oauth:grant-type:jwt-bearer', disabled: false },
    { key: 'assertion', value: assertion, disabled: false }
  ]

  return await get_token(urlencoded).then((data) => {
    const expires_at = Date.now() + (data.expires_in - 30) * 1000
    pm.environment.set('access_token', data.access_token)
    pm.environment.set('expires_at', expires_at)
  })
}

and we create the assertion like this:

  // libJSRSASign lib
  const libJSRSASign = pm.collectionVariables.get('libJSRSASign')
  /* eslint-disable no-global-assign */
  navigator = {}
  window = {}
  /* eslint-disable no-eval */

  eval(libJSRSASign)

  // UUID
  const uuid = require('uuid')

  const private_key_encrypted = pm.environment.get('private_key_encrypted').replace(/\\n/g, '')
  const private_key_passphrase = pm.environment.get('private_key_passphrase')

  const private_key = KEYUTIL.getKey(private_key_encrypted, private_key_passphrase)

  const kid = pm.environment.get('key_id')
  const iss = pm.environment.get('client_id')
  const sub = pm.environment.get('box_subject_id')
  const box_sub_type = pm.environment.get('box_subject_type')
  const aud = 'https://' + pm.collectionVariables.get('api.box.com') + '/oauth2/token'
  const jti = uuid.v4()
  // const exp = KJUR.jws.IntDate.get("now + 1minute")
  const exp = Math.floor(Date.now() / 1000) + 45
  const iat = KJUR.jws.IntDate.get('now')

  const header = { alg: 'RS512', typ: 'JWT', kid: kid }
  //   console.log(`header: ${JSON.stringify(header)}`)

  const claims =
      {
        iss: iss,
        sub: sub,
        box_sub_type: box_sub_type,
        aud: aud,
        jti: jti,
        exp: exp,
        iat: iat
      }

  // console.log(`claim set: ${JSON.stringify(claims)}`)

  const jwt = KJUR.jws.JWS.sign(null, header, claims, private_key)
  // console.log('JWT Assertion: ', jwt)

  return jwt
}

This is javascript inside Postman, so it might not be that helpful, but it does illustrate the process.

However I do have a rust example for you…

Fair warning, this is incomplete, poorly implemented, and not supported! you have been warned :wink:

https://crates.io/crates/rusty-box

In the examples you’ll find:

// use cargo run --example users_main to run this file
// use dotenv;

use rusty_box::{
    auth::auth_jwt::JWTAuth,
    auth::auth_jwt::SubjectType,
    client::{box_client::BoxClient, client_error::BoxAPIError},
    config::Config,
    rest_api::users::users_api,
};
use std::env;

#[tokio::main]
async fn main() -> Result<(), BoxAPIError> {
    dotenv::from_filename(".jwt.env").ok();

    let client_id = env::var("CLIENT_ID").expect("CLIENT_ID must be set");
    let client_secret = env::var("CLIENT_SECRET").expect("CLIENT_SECRET must be set");

    let env_subject_type = env::var("BOX_SUBJECT_TYPE").expect("BOX_SUBJECT_TYPE must be set");
    let box_subject_type = match env_subject_type.as_str() {
        "user" => SubjectType::User,
        "enterprise" => SubjectType::Enterprise,
        _ => panic!("BOX_SUBJECT_TYPE must be either 'user' or 'enterprise'"),
    };

    let box_subject_id = env::var("BOX_SUBJECT_ID").expect("BOX_SUBJECT_ID must be set");

    let public_key_id = env::var("PUBLIC_KEY_ID").expect("PUBLIC_KEY_ID must be set");

    let encrypted_private_key =
        env::var("ENCRYPTED_PRIVATE_KEY").expect("ENCRYPTED_PRIVATE_KEY must be set");

    let passphrase = env::var("PASSPHRASE").expect("PASSPHRASE must be set");

    let config = Config::new();
    let auth = JWTAuth::new(
        config,
        client_id,
        client_secret,
        box_subject_type,
        box_subject_id,
        public_key_id,
        encrypted_private_key,
        passphrase,
    );

    let mut client = BoxClient::new(Box::new(auth));

    let fields = vec![];

    let me = users_api::me(&mut client, Some(fields)).await?;
    println!("Me:\n{me:#?}\n");

    Ok(())
}

As for any differences on your environments, from the info you sent, I can’t see anything wrong with it. Of course I’m assuming you have a specific config.json file for each environment…

Hope this helps

Cheers