Authenticate a custom application with Drive and Photo Service (PDS) by using JSON Web Tokens (JWTs) to obtain an access token.
Overview
A JWT application is a custom application that uses JSON Web Token (JWT) for identity authentication. The JWT application signs data with a private key on its server side to produce a JWT string. This JWT string serves as a credential to access the PDS server, which validates it with the corresponding public key.

Use cases
Enterprise with an existing internal system: Your enterprise has an internal software system with its own account system. Users sign in through the internal login page and then access PDS features.
Enterprise with an existing login portal: Your enterprise has an independent account system and login portal. Combine the existing login portal with PDS to build a cloud storage system for your independent accounts.
Integration workflow
Create a custom domain and a JWT application in the PDS console.
Generate an RSA public-private key pair. Store the public key on the PDS server and the private key on the JWT application server.
The JWT application server encodes the data, signs it with the private key to produce a JWT Assertion string, and sends the string to the PDS server.
The PDS server validates the JWT Assertion with the public key and returns an access token. The JWT application server then uses this access token to call PDS APIs.
Prerequisites
Before you begin, ensure you have:
A PDS domain created in the PDS console.
A JWT application registered under that domain.
OpenSSL or another tool to generate RSA key pairs.
Node.js (if you plan to use the sample code in this guide).
Step 1: Configure keys
1.1 Create or select a domain

1.2 Create or select an application
Open the domain details page. On the Applications tab, create or select an application.

1.3 Set the public key
After you create or select an application, click Set Public Key.

Generate a public-private key pair.
Important: After you generate the key pair, copy and save the private key immediately. The private key is displayed only once. Then click OK.

Step 2: Obtain an access token
2.1 Generate the JWT string on the application server
Encode the data to sign and use the private key with the RS256 algorithm to generate a JWT string. The following Node.js example demonstrates this process:
const JWT = require('jsonwebtoken');
function signAssertion({ domain_id, client_id, user_id, privateKeyPEM }) {
var now_sec = parseInt(Date.now() / 1000);
var opt = {
iss: client_id,
sub: user_id,
sub_type: "user",
aud: domain_id,
jti: Math.random().toString(36).substring(2),
exp: now_sec + 60,
// iat: current time in seconds
// nbf: not before
auto_create: false,
};
return JWT.sign(opt, privateKeyPEM, {
algorithm: "RS256",
});
}JWT claim reference
Field | Required | Type | Description |
iss | Yes | String | The application ID. |
sub | Yes | String | The user ID or domain ID, depending on the |
sub_type | Yes | String | The account type. Valid values: |
aud | Yes | String | The domain ID. |
jti | Yes | String | A unique identifier for the JWT, generated by the application. Length: 16-128 characters. Use a UUID. |
exp | Yes | Integer | The JWT expiration time in Unix epoch seconds. The window between the effective time and expiration time must not exceed 15 minutes. To prevent clock skew between client and server, set this value to the current time plus 5 minutes. |
iat | No | Integer | The issuance time in Unix epoch seconds. The token cannot be used before this time. Example: |
nbf | No | Integer | The effective time in Unix epoch seconds. Defaults to the current time if not specified. The window between effective time and expiration time must not exceed 15 minutes. To prevent clock skew, set this value to the current time minus 5 minutes, or omit it. |
auto_create | No | Boolean | Whether to automatically create the user if the user does not exist. Default: |
For more information about JWT libraries and signing methods, see the JWT official website.
2.2 Exchange the JWT string for an access token
Call the Authorize operation to obtain an access token.
POST /v2/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&client_id=${APP_ID}&assertion=xxxxxxxxxxSet the request Content-Type to application/x-www-form-urlencoded. Place all request parameters in the request body.
Request parameters
Field | Required | Type | Description |
grant_type | Yes | String | The authorization grant type. Set this to the string literal: |
client_id | Yes | String | The application ID. |
assertion | Yes | String | The JWT string generated in Step 2.1. |
Sample response
{
"access_token": "eyJh****eQdnUTsEk4",
"refresh_token": "kL***Lt",
"expires_in": 7200,
"token_type": "Bearer"
}After the application server obtains the access token, return it to the application web client. Include the access token in all subsequent PDS API calls to access user resources.
2.3 Refresh the access token
A JWT-based access token is valid for 2 hours. After it expires, obtain a new one by using either of the following methods:
Re-generate: Repeat Steps 2.1 and 2.2 to generate a new access token.
Refresh (within 7 days): Call the Authorize operation with the refresh token to obtain a new access token. If more than 7 days have passed since expiration, repeat Steps 2.1 and 2.2 instead.
Call the Authorize operation to refresh the access token:
POST /v2/oauth/token
Content-Type: application/x-www-form-urlencoded
client_id=${APPID}&refresh_token=${access_token}&grant_type=refresh_token&redirect_uri=${REDIRECT_URI}Field | Required | Type | Description |
client_id | Yes | String | The application ID. |
refresh_token | Yes | String | The refresh token from the original token response. |
grant_type | Yes | String | The authorization grant type. Set this to the string literal: |
redirect_uri | Yes | String | The callback URL specified when the application was created. |
Step 3: Use Basic UI (optional)
If the official Basic UI meets your requirements and you do not need a custom UI, use Basic UI directly.
Method 1: Open Basic UI with window.open
Open the Basic UI with window.open and pass the access token through postMessage.
Sample code:
const endpoint = `https://${domain_id}.apps.aliyunpds.com`
const url = `${endpoint}/accesstoken?origin=${location.origin}`
var win = window.open(url)
window.addEventListener('message', onMessage, false)
async function onMessage(e) {
if (e.data.code == 'token' && e.data.message == 'ready') {
var result = await getToken(); // Obtain the access token from the server
//result = {"access_token": ...}
win.postMessage({
code: 'token',
message: result
}, endpoint || '*')
window.removeEventListener('message', onMessage)
}
}Method 2: Embed a custom login page in Basic UI
Basic UI embeds a custom login page through an iframe.
In the system configuration, set the URL of the custom login page and the JWT application ID. This allows Basic UI to auto-refresh the token.

When a user signs in, the iframe displays the custom login page instead of the default Basic UI login page.
After the user signs in successfully, pass the token to the parent page through postMessage:

if(parent!=self){
let origin = ''
parent.postMessage({
code: 'token',
message: {
access_token: 'xxxx',
refresh_token: 'xxxx',
...
}
}, endpoint || "*")
}Appendix 1: Complete Node.js implementation
The following sample code demonstrates how to obtain and refresh an access token in a JWT application:
const fs = require('fs')
const JWT = require('jsonwebtoken');
const axios = require('axios')
const DOMAIN_ID = '' // Domain ID
const APP_ID = '' // Application ID
const USER_ID = '' // User UID
const PRIVATE_KEY_PEM = '' // Private key configured in Step 1.3
const PRE = `https://${domain_id}.api.aliyunpds.com`
async function init() {
try {
// Replace these variables with your actual values
var params = {
domain_id: DOMAIN_ID,
client_id: APP_ID,
user_id: USER_ID,
privateKeyPEM: PRIVATE_KEY_PEM,
};
var assertion = signAssertion(params)
var obj = await getToken(assertion)
return obj.data
} catch (e) {
if (e.response) {
console.log(e.response.status)
console.log(e.response.headers)
console.log(e.response.data)
} else {
console.error(e)
}
}
}
function signAssertion({ domain_id, client_id, user_id, privateKeyPEM }) {
var now_sec = parseInt(Date.now()/1000)
var opt = {
iss: client_id,
sub: user_id,
sub_type: 'user',
aud: domain_id,
jti: Math.random().toString(36).substring(2),
exp: now_sec + 300,
// iat: current time in seconds
// nbf: not before
auto_create: true,
};
return JWT.sign(opt, privateKeyPEM, {
algorithm: 'RS256'
});
}
async function getToken(assertion) {
return await axios({
method: 'post',
url: PRE + '/v2/oauth/token',
// Set the Content-Type to application/x-www-form-urlencoded
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
// Place request parameters in the body
data: params({
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
client_id: APP_ID,
assertion
})
})
}
async function refreshToken(refresh_token) {
return await axios({
method: 'post',
url: PRE + '/v2/oauth/token',
// Set the Content-Type to application/x-www-form-urlencoded
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
// Place request parameters in the body
data: params({
grant_type: 'refresh_token',
client_id: APP_ID,
refresh_token,
})
})
}
function params(m){
const params = new URLSearchParams();
for(var k in m){
params.append(k, m[k]);
}
return params;
}
// Test call
;(async ()=>{
let result = await init()
console.log(result) // Returns a token object {access_token:...}. See Appendix 2 for the object structure.
// After the access token expires
refreshToken(result.refreshToken) // Returns a new token object {access_token:...}. See Appendix 2 for the object structure.
})();
Appendix 2: Token object structure
Sample data:
{
"access_token": "eyJhbG.....g7M0p28",
"refresh_token": "62f1acc.......9b781f3",
"expires_in": 7200,
"token_type": "Bearer"
}For more information about the response parameters, see Obtain an access token.