Function Compute supports signature authentication for HTTP triggers. When a request reaches the Function Compute gateway, the gateway validates the request signature before invoking your function — your function code does not need to re-authenticate the request.
When to use signature authentication: Choose signature authentication if only callers with a valid Alibaba Cloud AccessKey should invoke the trigger. For publicly accessible triggers, leave authentication disabled and handle authorization in your function code instead.
Prerequisites
Before you begin, ensure that you have:
A Function Compute function with an HTTP trigger. To create one, see Create a Function
An Alibaba Cloud AccessKey ID and AccessKey Secret
Enable signature authentication for an HTTP trigger
Log on to the Function Compute console. In the left-side navigation pane, click Functions.
In the top navigation bar, select a region. On the Functions page, click the function you want to manage.
On the function configuration page, click the Trigger tab. In the Actions column next to the target trigger, click Modify.
In the Edit Trigger panel, set Authentication Method to Signature Authentication, then click OK.
Call an HTTP trigger with signature authentication
HTTP triggers use the Alibaba Cloud SDK signature mechanism (ACS3-HMAC-SHA256). For the full specification, see Signature Mechanism.
How it works
To call a signature-authenticated HTTP trigger:
Generate a signature string using the Alibaba Cloud signing SDK.
Set the signature string in the
Authorizationheader of the HTTP request.Send the request with any HTTP client.
Required headers:
| Header | Required | Description |
|---|---|---|
x-acs-date | Yes | Current UTC time in ISO 8601 format. Must be set before generating the signature. |
x-acs-security-token | When using Security Token Service (STS) | Temporary security token from STS. Pass an empty string if not using STS. |
authorization | Yes | The signature string generated by the signing SDK. Set this after generating the signature. |
Install the signing SDK
| Language | Installation |
|---|---|
| Go | go get github.com/alibabacloud-go/openapi-util/service |
| Python | pip install alibabacloud-openapi-util |
| Node.js | npm install @alicloud/openapi-util |
| Java (Maven) | See the dependency snippet below |
Java Maven dependencies:
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>openapiutil</artifactId>
<version>0.2.1</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>tea-openapi</artifactId>
<version>0.3.1</version>
</dependency>Code examples
All examples below sign a POST request using ACS3-HMAC-SHA256 and set the result in the authorization header. Replace https://xx.cn-shanghai.fcapp.run/hello?foo=bar with your HTTP trigger address.
<details> <summary>Python</summary>
# -*- coding: utf-8 -*-
import os
from datetime import datetime
from urllib.parse import urlparse, parse_qs
import requests
from alibabacloud_openapi_util.client import Client as util
from Tea.request import TeaRequest
accessKeyId = os.environ['ALIBABA_CLOUD_ACCESS_KEY_ID']
accessKeySecret = os.environ['ALIBABA_CLOUD_ACCESS_KEY_SECRET']
securityToken = os.environ.get('ALIBABA_CLOUD_SECURITY_TOKEN', '') # Optional. Required when using STS.
# The method parameter must be uppercase, such as POST or GET. If your request method is GET, change requests.post to requests.get below.
method = 'POST'
body = 'hello world'
url = 'https://xx.cn-shanghai.fcapp.run/hello?foo=bar' # Your HTTP trigger address
date = datetime.utcnow().isoformat('T')[:19]+'Z'
headers = {
'x-acs-date': date,
'x-acs-security-token': securityToken
}
parsedUrl = urlparse(url)
authRequest = TeaRequest()
authRequest.method = method
authRequest.pathname = parsedUrl.path.replace('$', '%24')
authRequest.headers = headers
authRequest.query = {k: v[0] for k, v in parse_qs(parsedUrl.query).items()}
auth = util.get_authorization(authRequest, 'ACS3-HMAC-SHA256', '', accessKeyId, accessKeySecret)
headers['authorization'] = auth
# If the method is GET, change requests.post to requests.get below.
resp = requests.post(url, body, headers=headers)
print(resp.text)</details>
<details> <summary>Java</summary>
package com.aliyun.sample;
import org.apache.http.NameValuePair;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import com.aliyun.tea.*;
public class Sample {
public static void main(String[] args_) throws Exception {
String accessKeyId = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
String accessKeySecret = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
String securityToken = System.getenv("ALIBABA_CLOUD_SECURITY_TOKEN"); // Optional. Required when using STS.
if (securityToken == null) {
securityToken = "";
}
// The method parameter must be uppercase, such as POST or GET. If your request method is GET, change HttpPost to HttpGet below.
String method = "POST";
String body = "hello world";
String url = "https://xx.cn-shanghai.fcapp.run/hello?foo=bar"; // Your HTTP trigger address
Map<String, String> headers = new HashMap<String, String>();
String date = Instant.now().toString();
headers.put("x-acs-date", date);
headers.put("x-acs-security-token", securityToken);
URI uri = new URI(url);
Map<String, String> query = new HashMap<String, String>();
for (NameValuePair pair : URLEncodedUtils.parse(uri, StandardCharsets.UTF_8)) {
query.put(pair.getName(), pair.getValue());
}
TeaRequest req = new TeaRequest();
req.method = method;
req.pathname = uri.getPath().replace("$", "%24");
req.headers = headers;
req.query = query;
String auth = com.aliyun.openapiutil.Client.getAuthorization(
req, "ACS3-HMAC-SHA256", "", accessKeyId, accessKeySecret);
headers.put("authorization", auth);
// If the method is GET, change HttpPost to HttpGet below.
HttpPost request = new HttpPost(url);
for (Map.Entry<String, String> entry : headers.entrySet()) {
request.setHeader(entry.getKey(), entry.getValue());
}
StringEntity entity = new StringEntity(body);
request.setEntity(entity);
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
org.apache.http.HttpResponse response = httpClient.execute(request);
String responseString = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
System.out.println(responseString);
}
}
}</details>
<details> <summary>Go</summary>
package main
import (
"io/ioutil"
"log"
"net/http"
"os"
"strings"
"time"
openapiutil "github.com/alibabacloud-go/openapi-util/service"
"github.com/alibabacloud-go/tea/tea"
)
func main() {
accessKeyId := tea.String(os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
accessKeySecret := tea.String(os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"))
securityToken := tea.String(os.Getenv("ALIBABA_CLOUD_SECURITY_TOKEN")) // Optional. Required when using STS.
// The method parameter must be uppercase.
method := "POST"
body := "hello world"
url := "https://xx.cn-shanghai.fcapp.run/hello?foo=bar" // Your HTTP trigger address
req, err := http.NewRequest(method, url, strings.NewReader(body))
if err != nil {
log.Printf("new request error: %v", err)
return
}
date := time.Now().UTC().Format(time.RFC3339)
req.Header.Set("x-acs-date", date)
req.Header.Set("x-acs-security-token", *securityToken)
authRequest := &tea.Request{
Method: &method,
Pathname: tea.String(strings.ReplaceAll(req.URL.Path, "$", "%24")),
Headers: make(map[string]*string),
Query: make(map[string]*string),
}
for k := range req.URL.Query() {
authRequest.Query[k] = tea.String(req.URL.Query().Get(k))
}
for k := range req.Header {
authRequest.Headers[k] = tea.String(req.Header.Get(k))
}
auth := openapiutil.GetAuthorization(authRequest, tea.String("ACS3-HMAC-SHA256"), nil, accessKeyId, accessKeySecret)
req.Header.Set("authorization", *auth)
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Printf("post error: %v", err)
return
}
defer resp.Body.Close()
buf, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Printf("read body error: %v", err)
return
}
log.Printf("resp: %v, body: %s", resp, string(buf))
}</details>
<details> <summary>Node.js</summary>
const util = require("@alicloud/openapi-util");
const axios = require('axios');
async function main() {
const accessKeyId = process.env['ALIBABA_CLOUD_ACCESS_KEY_ID'];
const accessKeySecret = process.env['ALIBABA_CLOUD_ACCESS_KEY_SECRET'];
const securityToken = process.env['ALIBABA_CLOUD_SECURITY_TOKEN'] || ''; // Optional. Required when using STS.
// The method parameter must be uppercase, such as POST or GET. If your request method is GET, change axios.post to axios.get below.
const method = 'POST';
const body = 'hello world';
const url = 'https://xx.cn-shanghai.fcapp.run/hello?foo=bar'; // Your HTTP trigger address
const date = new Date().toISOString();
let headers = {
'x-acs-date': date,
'x-acs-security-token': securityToken
};
const parsedUrl = new URL(url);
const authRequest = {
method: method,
pathname: parsedUrl.pathname.replace('$', '%24'),
headers: headers,
query: Object.fromEntries(parsedUrl.searchParams),
};
const auth = util.default.getAuthorization(authRequest, 'ACS3-HMAC-SHA256', '', accessKeyId, accessKeySecret);
headers['authorization'] = auth;
// If the method is GET, change axios.post to axios.get below.
const resp = await axios.post(url, body, {
headers: headers,
});
console.log('resp: ', resp.data);
}
main().catch(console.error);</details>
Verify the setup
This procedure uses Go to verify that signature authentication works end to end.
Install the Go signing SDK:
go get github.com/alibabacloud-go/openapi-util/serviceSave the Go example above as
main.go. Update theurlvariable with your HTTP trigger address.Run the code:
go run main.go
A successful response looks similar to:
2024/02/22 17:21:31 resp: &{200 OK 200 HTTP/1.1 1 1 map[Access-Control-Expose-Headers:[Date,x-fc-request-id] Content-Disposition:[attachment] Content-Length:[14] Content-Type:[text/plain; charset=utf-8] Date:[Thu, 22 Feb 2024 09:21:31 GMT] X-Fc-Request-Id:[1-65d71219-15d63510-fecf237c590c]] 0xc000120040 14 [] false false map[] 0xc000100100 0xc0000e0370}, body: Hello, Golang!Troubleshooting
"required HTTP header Date was not specified"
Cause: The request has no valid signature, or the x-acs-date header is missing.
Resolution: Set the x-acs-date header to the current UTC time before calling the signing SDK, then add the returned signature to the authorization header.
"the difference between the request time '...' and the current time '...' is too large"
Cause: The signature has expired. Signatures are time-bound to prevent replay attacks.
Resolution: Re-sign the request using the current timestamp and retry immediately.
"The request signature we calculated does not match the signature you provided. Check your access key and signing method"
Cause: The signature in the request does not match the one Function Compute calculated.
Resolution: Check the following:
The AccessKey ID and AccessKey Secret are correct and belong to the same account.
The signing algorithm is
ACS3-HMAC-SHA256.The request method, path, headers, and query parameters passed to the signing SDK match the actual HTTP request sent.