This topic describes a common security pattern where a mobile app uses Security Token Service (STS) tokens to directly access Alibaba Cloud resources.
Background information
In this scenario, an enterprise develops a mobile app that must upload and download data directly to and from Object Storage Service (OSS). The app runs on end-user mobile devices, which are considered untrusted client environments.
The solution must meet the following requirements:
Direct access to OSS: To reduce latency and server load, the app must communicate directly with OSS instead of proxying traffic through a backend app server.
Secure credential management: To mitigate security risks, long-term credentials (AccessKey pairs) must not be embedded in the mobile app.
Least-privilege access: To minimize the potential impact of a security breach, each app client must be granted only the permissions necessary to perform its tasks, and only for a limited time.
Solution
This solution uses the token vending machine (TVM) pattern. The mobile app requests STS tokens from a trusted backend app server. The server, acting as a RAM user, calls the AssumeRole API operation of STS to assume a pre-defined RAM role. STS returns an STS token, which the server passes to the mobile app. The app then uses the STS token to sign and send requests directly to OSS.

The mobile app authenticates with the app server and requests an STS token.
Create a RAM role and attach a policy that grants the required permissions (such as, access to a specific OSS bucket).
Create a RAM user for the app server and grant it permission to assume the RAM role.
The app server calls the AssumeRole operation of STS to obtain an STS token.
(Optional) The app server can pass a session policy in the
AssumeRolecall to further scope down the permissions of the STS tokens. This allows for fine-grained access control tailored to each user or device.The mobile app uses the received STS tokens to sign and send requests directly to OSS. For more information, see Use a STS token to access OSS.
Create a RAM role and grant permissions
This section assumes the Alibaba Cloud account ID is 123456789012****.
Create a RAM role named
oss-objectmanager. For the principal type, choose Cloud Account.NoteWhen you create the RAM role, select Current Account as the trusted account. This configures the role's trust policy to allow only principals from within the same Alibaba Cloud account to assume it.
For more information, see Create a RAM role for a trusted Alibaba Cloud account.
After the RAM role is created, you can view the following information on its details page:
The ARN of the RAM role:
acs:ram::123456789012****:role/oss-objectmanager.The trust policy for the RAM role:
NoteThe
Principalin the trust policy specifies which identities are allowed to assume the role. In this case, "acs:ram::123456789012****:root" means any authenticated identity within account123456789012****can assume the RAM role, subject to their own RAM policies.{ "Statement": [ { "Action": "sts:AssumeRole", "Effect": "Allow", "Principal": { "RAM": [ "acs:ram::123456789012****:root" ] } } ], "Version": "1" }
Create a policy for the RAM role. For this example, attach the
AliyunOSSFullAccesssystem policy to theoss-objectmanagerRAM role.
Create a RAM user and grant it permission to assume a RAM role
Create a RAM user named
appserver, which will represent your app server.Attach the
AliyunSTSAssumeRoleAccesssystem policy to theappserveruser to allow it to call theAssumeRoleAPI operation. For more information, see Grant permissions to a RAM user.
Obtain STS tokens
The app server uses the AccessKey pair of the
appserverRAM user to call the AssumeRole API operation of STS.NoteBefore using the Alibaba Cloud CLI, you must install and configure it on your app server with the AccessKey pair of the
appserverRAM user. For more information, see STS CLI reference.In this example, the
Policyparameter is not included in the call. Therefore, the returned STS token has the same permissions that are granted by theoss-objectmanagerrole's RAM policy. To return STS tokens with more restrictive permissions, see the next section.
The following example shows how to use Alibaba Cloud CLI to call the AssumeRole operation:
aliyun sts AssumeRole --RoleArn 'acs:ram::123456789012****:role/oss-objectmanager' --RoleSessionName 'client-001'STS returns a STS token, which include a temporary
AccessKeyId, aAccessKeySecret, and aSecurityToken.Sample response:
{ "AssumedRoleUser": { "AssumedRoleId": "391578752573****:client-001", "Arn": "acs:ram::123456789012****:role/oss-objectmanager/client-001" }, "Credentials": { "AccessKeySecret": "yourAccessKeySecret", "SecurityToken": "yourSecurityToken", "Expiration": "2016-01-13T15:02:37Z", "AccessKeyId": "yourAccessKeyId" }, "RequestId": "E1779AAB-E7AF-47D6-A9A4-53128708B6CE" }NoteThe
SecurityTokenexpires after a specified duration. If the mobile app requires access for longer than the token's lifetime, your application must implement logic to refresh the STS token by requesting a new one from the app server before the old one expires.
Limit the permissions of STS tokens
When you call AssumeRole, you can include the Policy parameter to pass a session policy. This policy scopes down the permissions of the returned STS tokens. The resulting permissions are the intersection of the role's RAM policy and the session policy.
In this example, the session policy grants permission to download the sample-bucket/2015/01/01/*.jpgobject.
Sample request
aliyun sts AssumeRole --RoleArn 'acs:ram::123456789012****:role/oss-objectmanager' --RoleSessionName 'client-002' --Policy '{"Version":"1", "Statement": [{"Effect":"Allow", "Action":"oss:GetObject", "Resource":"acs:oss:*:*:sample-bucket/2015/01/01/*.jpg"}]}'NoteBy default, STS tokens are valid for 3,600 seconds (1 hour). You can use the
DurationSecondsparameter to specify a shorter or longer duration, up to the maximum session duration configured for the role. For more information, see AssumeRole.Sample response
{ "AssumedRoleUser": { "AssumedRoleId": "391578752573****:client-002", "Arn": "acs:ram::123456789012****:role/oss-objectmanager/client-002" }, "Credentials": { "AccessKeySecret": "yourAccessKeySecret", "SecurityToken": "yourSecurityToken", "Expiration": "2016-01-13T15:03:39Z", "AccessKeyId": "yourAccessKeyId" }, "RequestId": "98835D9B-86E5-4BB5-A6DF-9D3156ABA567" }
Use the STS tokens to access OSS
The app server returns the STS tokens (AccessKey ID, AccessKey secret, and security token) to the mobile app.
The mobile app uses the STS tokens to configure the OSS SDK and make authenticated requests to OSS.
This example demonstrates how to integrate the OSS Android SDK into an Android app and use an STS token to download an object from OSS to a local device. Sample code:
// Set the endpoint for the region where your OSS bucket is located. For example, for China (Hangzhou), use https://oss-cn-hangzhou.aliyuncs.com. String endpoint = "yourEndpoint"; // Set the region where your bucket is located. For example, for China (Hangzhou), use "cn-hangzhou". String region = "yourRegion"; // The AccessKey ID and AccessKey secret obtained from your STS. String accessKeyId = "yourAccessKeyId"; String accessKeySecret = "yourAccessKeySecret"; // The security token obtained from STS. String securityToken = "yourSecurityToken"; OSSCredentialProvider credentialProvider = new OSSStsTokenCredentialProvider(accessKeyId, accessKeySecret, securityToken); ClientConfiguration config = new ClientConfiguration(); config.setSignVersion(SignVersion.V4); // Create the OSSClient instance. OSSClient oss = new OSSClient(getApplicationContext(), endpoint, credentialProvider); oss.setRegion(region); // Initiate the asynchronous object download. GetObjectRequest get = new GetObjectRequest("sample-bucket", "2015/01/01/grass.jpg"); OSSAsyncTask task = oss.asyncGetObject(get, new OSSCompletedCallback<GetObjectRequest, GetObjectResult>() { @Override public void onSuccess(GetObjectRequest request, GetObjectResult result) { // The request was successful. Log.d("asyncGetObject", "DownloadSuccess"); Log.d("Content-Length", "" + result.getContentLength()); try (InputStream inputStream = result.getObjectContent()) { byte[] buffer = new byte[2048]; int len; while ((len = inputStream.read(buffer)) != -1) { // Get the input stream from the result and process the downloaded data. } } catch (IOException e) { e.printStackTrace(); } } @Override // The onFailure callback is triggered when the request fails. public void onFailure(GetObjectRequest request, ClientException clientExcepion, ServiceException serviceException) { // Handle the request failure. if (clientExcepion != null) { // Handle client-side exceptions, such as network errors. clientExcepion.printStackTrace(); } if (serviceException != null) { // Handle service-side exceptions from OSS. Log.e("ErrorCode", serviceException.getErrorCode()); Log.e("RequestId", serviceException.getRequestId()); Log.e("HostId", serviceException.getHostId()); Log.e("RawMessage", serviceException.getRawMessage()); } } }); // To cancel the download task, call task.cancel(). // task.cancel(); // To block until the download is finished, call task.waitUntilFinished(). // task.waitUntilFinished();