Downscope a Token

Downscope a Token

Downscoping is a way to exchange an existing Access Token for a new one that is more restricted.

Reasons to downscope

An application might need to share the Access Token with an environment that it does not fully control. A common example of this would be when using Box UI Elements in a web browser.

When an application needs to pass an Access Token to the browser there is a potential security risk that needs to be resolved. In order to limit this risk the Access Token can be exchanged for a new token with much stricter permissions.

High-level overview

A downscoped token is a token that has fewer permissions (scopes) than the original token, as well as the optional additional restriction to only allow access to a specific file.

Downscoping overview

The new token takes the permissions of the original token and restricts them to the tokens passed in, as well as the resource provided.

Downscoping in practice

To downscope a token, pass the POST /oauth2/token endpoint an existing Access Token, a list of scopes, as well as an optional file URL to restrict the token to.

cURL
curl -i -X POST "https://api.box.com/oauth2/token" \
     -H "content-type: application/x-www-form-urlencoded" \
     -d "subject_token=[ACCESS_TOKEN]" \
     -d "subject_token_type=urn:ietf:params:oauth:token-type:access_token" \
     -d "scope=item_upload item_preview base_explorer" \
     -d "resource=https://api.box.com/2.0/folders/123456" \
     -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange"
TypeScript Gen
let resource = 'https://api.box.com/2.0/files/123456789';
let token = await oauth.downscopeToken(['item_preview'], resource);
const auth = new BoxDeveloperTokenAuth({ token: token.accessToken });
const client = new BoxClient({ auth });
Python Gen
from box_sdk_gen import BoxDeveloperTokenAuth, AccessToken, BoxClient

resource = "https://api.box.com/2.0/files/123456789"
downscoped_token: AccessToken = auth.downscope_token(
    scopes=["item_preview"],
    resource=resource,
)
downscoped_auth = BoxDeveloperTokenAuth(token=downscoped_token.access_token)
client = BoxClient(auth=downscoped_auth)
.NET Gen
using Box.Sdk.Gen;

resourcePath = 'https://api.box.com/2.0/files/123456789'
AccessToken downscopedToken = await auth.DownscopeTokenAsync(
  scopes: Array.AsReadOnly(new [] {"item_rename","item_preview"}), resource: resourcePath
);

BoxClient downscopedClient = new BoxClient(auth: new BoxDeveloperTokenAuth(token: downscopedToken.AccessTokenField));
Swift Gen (Beta)
let resource = "https://api.box.com/2.0/files/123456789"
let downscopedToken: AccessToken = try await auth.downscopeToken(scopes: ["item_preview"], resource: resource)
let downscopedAuth = BoxDeveloperTokenAuth(token: downscopedToken.accessToken!)
let downscopedClient = BoxClient(auth: downscopedAuth)
Java
BoxAPIConnection api = new BoxAPIConnection("YOUR-ACCESS-TOKEN");

String resource = "https://api.box.com/2.0/files/RESOURCE-ID";
List<String> scopes = new ArrayList<String>();
scopes.add("item_preview");
scopes.add("item_content_upload");

ScopedToken token = api.getLowerScopedToken(scopes, resource);
Python
target_file = client.file(file_id='FILE_ID_HERE')
token_info = client.downscope_token(['item_preview'], target_file)
downscoped_client = Client(
  OAuth2(
    client_id=None,
    client_secret=None,
    access_token=token_info.access_token
  )
)
.NET
var exchanger = new TokenExchange(client.Auth.Session.AccessToken, "item_preview");
exchanger.SetResource("https://api.box.com/2.0/files/123456789");
string downscopedToken = await exchanger.ExchangeAsync();
Node
client.exchangeToken('item_preview', 'https://api.box.com/2.0/files/123456789')
	.then(tokenInfo => {
		// tokenInfo.accessToken contains the new downscoped access token
	});
ParameterDescription
subject_tokenThe original token to downscope. This can be a token that was acquired through OAuth 2.0, JWT token exchange, or as an App Token.
scopeA space-delimited list of scopes to limit the new token to. Any valid scope for the application can be used, though a special set of scopes for Box UI elements is available
resourceAn optional full URL path to the file the token should be restricted to.
box_shared_linkAn optional shared link URL for a file or folder on Box. Password protected links are not supported. This option cannot be used in addition to the resource option nor can it be a shared link created on a weblink.
subject_token_typeAlways set to urn:ietf:params:oauth:token-type:access_token
grant_typeAlways set to urn:ietf:params:oauth:grant-type:token-exchange

Downscoped Access Token Object

A downscoped Access Token returned by the POST /oauth2/token endpoint contains extra information on the specific restrictions.

{
  "access_token": "1!DgsZ6V9kMWZu2StrxwQDF5BudQNen-xUmU2cfcVKArE....",
  "expires_in": 4175,
  "token_type": "bearer",
  "restricted_to": [
    {
      "scope": "item_preview",
      "object": {
        "type": "folder",
        "id": "1234567890",
        "sequence_id": "0",
        "etag": "0",
        "name": "Test"
      }
    }
  ],
  "issued_token_type": "urn:ietf:params:oauth:token-type:access_token"
}

Most importantly here is the list of restricted_to entries that will contain each combination of object and scope that the new token has the permissions for.

A downscoped token does not include a refresh token. To get a new downscoped token, refresh the original refresh token and use that new token to get a downscoped token.