diff --git a/docs/resources/live_url_authentication.md b/docs/resources/live_url_authentication.md
new file mode 100644
index 0000000000..11fd4d9033
--- /dev/null
+++ b/docs/resources/live_url_authentication.md
@@ -0,0 +1,79 @@
+---
+subcategory: "Live"
+layout: "huaweicloud"
+page_title: "HuaweiCloud: huaweicloud_live_url_authentication"
+description: |-
+ Manages an URL authentication resource within HuaweiCloud.
+---
+
+# huaweicloud_live_url_authentication
+
+Manages an URL authentication resource within HuaweiCloud.
+
+-> The current resource is a one-time resource, and destroying this resource will not change the current status.
+
+-> Before creating the resource, you need to cofiguration the URL validation first (creating
+ the URL validation resource first).
+
+## Example Usage
+
+```hcl
+variable "domain_name" {}
+variable "type" {}
+variable "stream_name" {}
+variable "app_name" {}
+
+resource "huaweicloud_live_url_authentication" "test" {
+ domain_name = var.domain_name
+ type = var.type
+ stream_name = var.stream_name
+ app_name = var.app_name
+}
+```
+
+## Argument Reference
+
+The following arguments are supported:
+
+* `region` - (Optional, String, ForceNew) Specifies the region in which to create the resource.
+ If omitted, the provider-level region will be used.
+ Changing this parameter will create a new resource.
+
+* `domain_name` - (Required, String, ForceNew) Specifies the domain name to which the URL validation belongs.
+ Including the ingest domain name and streaming domain name.
+ Changing this parameter will create a new resource.
+
+* `type` - (Required, String, ForceNew) Specifies the type of the domain name.
+ The valid values are as follow:
+ + **push**: Indicates ingest domain name.
+ + **pull**: Indicates streaming domain name.
+
+ Changing this parameter will create a new resource.
+
+* `stream_name` - (Required, String, ForceNew) Specifies the stream name.
+ Changing this parameter will create a new resource.
+
+* `app_name` - (Required, String, ForceNew) Specifies the application name.
+ Changing this parameter will create a new resource.
+
+* `check_level` - (Optional, String, ForceNew) Specifies the check level.
+ Changing this parameter will create a new resource.
+ The valid values are as follows:
+ + **3**: Indicates only checks the LiveID matches or not but not the validity of the signed URL.
+ + **5**: Indicates checks the LiveID matches or not and the validity of timestamp.
+
+ -> 1.If the value of the `auth_type` in the URL validation is set to **c_aes**, this parameter is mandatory.
+
2.The value of the LiveID consists of `app_name` and `stream_name`: /.
+
+* `start_time` - (Optional, String, ForceNew) Specifies the start time of the valid access time defined by the user.
+ Changing this parameter will create a new resource.
+ The time is in UTC, the format is **yyyy-mm-ddThh:mm:ssZ**. e.g. **2024-06-01T15:03:01Z**
+ If this parameter is not specified or is left empty, the current time is used by default.
+
+## Attribute Reference
+
+In addition to all arguments above, the following attributes are exported:
+
+* `id` - The resource ID, UUID format.
+
+* `key_chain` - The generated signed URLs.
diff --git a/huaweicloud/provider.go b/huaweicloud/provider.go
index 31d9a82485..f42c3ca8c9 100644
--- a/huaweicloud/provider.go
+++ b/huaweicloud/provider.go
@@ -1859,6 +1859,7 @@ func Provider() *schema.Provider {
"huaweicloud_live_referer_validation": live.ResourceRefererValidation(),
"huaweicloud_live_snapshot": live.ResourceLiveSnapshot(),
"huaweicloud_live_transcoding": live.ResourceTranscoding(),
+ "huaweicloud_live_url_authentication": live.ResourceUrlAuthentication(),
"huaweicloud_live_url_validation": live.ResourceUrlValidation(),
"huaweicloud_live_channel": live.ResourceChannel(),
"huaweicloud_live_https_certificate": live.ResourceHTTPSCertificate(),
diff --git a/huaweicloud/services/acceptance/live/resource_huaweicloud_live_url_authentication_test.go b/huaweicloud/services/acceptance/live/resource_huaweicloud_live_url_authentication_test.go
new file mode 100644
index 0000000000..7c3207b4da
--- /dev/null
+++ b/huaweicloud/services/acceptance/live/resource_huaweicloud_live_url_authentication_test.go
@@ -0,0 +1,97 @@
+package live
+
+import (
+ "fmt"
+ "testing"
+ "time"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
+
+ "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/services/acceptance"
+)
+
+func TestAccUrlAuthentication_basic(t *testing.T) {
+ var (
+ rName1 = "huaweicloud_live_url_authentication.test"
+ rName2 = "huaweicloud_live_url_authentication.try"
+ ingestDomainName = fmt.Sprintf("%s.huaweicloud.com", acceptance.RandomAccResourceNameWithDash())
+ streamDomainName = fmt.Sprintf("%s.huaweicloud.com", acceptance.RandomAccResourceNameWithDash())
+ startTime = time.Now().UTC().Add(24 * time.Hour).Format("2006-01-02T15:04:05Z")
+ )
+ // Avoid CheckDestroy because this resource is a one-time action resource and there is nothing in the destroy method.
+ // lintignore:AT001
+ resource.ParallelTest(t, resource.TestCase{
+ PreCheck: func() {
+ acceptance.TestAccPreCheck(t)
+ },
+ ProviderFactories: acceptance.TestAccProviderFactories,
+ Steps: []resource.TestStep{
+ {
+ // The action resource do not need to be checked and no processing is performed in the Read method.
+ Config: testAccUrlAuthentication_ingest(ingestDomainName, startTime),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(rName1, "key_chain.#", "1"),
+ ),
+ },
+ {
+ // The action resource do not need to be checked and no processing is performed in the Read method.
+ Config: testAccUrlAuthentication_stream(streamDomainName),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(rName2, "key_chain.#", "3"),
+ ),
+ },
+ },
+ })
+}
+
+func testAccUrlAuthentication_ingest(name, startTime string) string {
+ return fmt.Sprintf(`
+resource "huaweicloud_live_domain" "test" {
+ name = "%[1]s"
+ type = "push"
+}
+
+resource "huaweicloud_live_url_validation" "test" {
+ domain_name = huaweicloud_live_domain.test.name
+ key = "IbBIzklRGCyMEd18oPV9sxAuuwNIzT81"
+ auth_type = "c_aes"
+ timeout = 1000
+}
+
+resource "huaweicloud_live_url_authentication" "test" {
+ domain_name = huaweicloud_live_domain.test.name
+ type = "push"
+ app_name = "live1"
+ stream_name = "tf-test"
+ check_level = 3
+ start_time = "%[2]s"
+
+ depends_on = [huaweicloud_live_url_validation.test]
+}
+`, name, startTime)
+}
+
+func testAccUrlAuthentication_stream(name string) string {
+ return fmt.Sprintf(`
+resource "huaweicloud_live_domain" "try" {
+ name = "%s"
+ type = "pull"
+}
+
+resource "huaweicloud_live_url_validation" "try" {
+ domain_name = huaweicloud_live_domain.try.name
+ key = "IbBIzklRGCyMEd18oPV9sxAuuwNIzT81"
+ auth_type = "d_sha256"
+ timeout = 800
+}
+
+resource "huaweicloud_live_url_authentication" "try" {
+ domain_name = huaweicloud_live_domain.try.name
+ type = "pull"
+ app_name = "live2"
+ stream_name = "tf-try"
+
+ depends_on = [huaweicloud_live_url_validation.try]
+}
+`, name)
+}
diff --git a/huaweicloud/services/live/resource_huaweicloud_live_url_authentication.go b/huaweicloud/services/live/resource_huaweicloud_live_url_authentication.go
new file mode 100644
index 0000000000..2c46ace24c
--- /dev/null
+++ b/huaweicloud/services/live/resource_huaweicloud_live_url_authentication.go
@@ -0,0 +1,148 @@
+package live
+
+import (
+ "context"
+ "strings"
+
+ "github.com/hashicorp/go-multierror"
+ "github.com/hashicorp/go-uuid"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+
+ "github.com/chnsz/golangsdk"
+
+ "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/config"
+ "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/utils"
+)
+
+// @API Live POST /v1/{project_id}/auth/chain
+func ResourceUrlAuthentication() *schema.Resource {
+ return &schema.Resource{
+ CreateContext: resourceUrlAuthenticationCreate,
+ ReadContext: resourceUrlAuthenticationRead,
+ DeleteContext: resourceUrlAuthenticationDelete,
+
+ Schema: map[string]*schema.Schema{
+ "region": {
+ Type: schema.TypeString,
+ Optional: true,
+ Computed: true,
+ ForceNew: true,
+ },
+ "domain_name": {
+ Type: schema.TypeString,
+ Required: true,
+ ForceNew: true,
+ Description: `Specifies the domain name.`,
+ },
+ "type": {
+ Type: schema.TypeString,
+ Required: true,
+ ForceNew: true,
+ Description: `Specifies the type of the domain name.`,
+ },
+ "stream_name": {
+ Type: schema.TypeString,
+ Required: true,
+ ForceNew: true,
+ Description: `Specifies the stream name.`,
+ },
+ "app_name": {
+ Type: schema.TypeString,
+ Required: true,
+ ForceNew: true,
+ Description: `Specifies the application name.`,
+ },
+ "check_level": {
+ Type: schema.TypeInt,
+ Optional: true,
+ ForceNew: true,
+ Description: `Specifies the check level.`,
+ },
+ "start_time": {
+ Type: schema.TypeString,
+ Optional: true,
+ ForceNew: true,
+ Description: `Specifies the start time of the valid access time defined by the user.`,
+ },
+ "key_chain": {
+ Type: schema.TypeList,
+ Computed: true,
+ Elem: &schema.Schema{Type: schema.TypeString},
+ Description: `The generated signed URLs.`,
+ },
+ },
+ }
+}
+
+func buildSignUrlBodyParams(d *schema.ResourceData) map[string]interface{} {
+ return map[string]interface{}{
+ "domain": d.Get("domain_name"),
+ "domain_type": d.Get("type"),
+ "stream": d.Get("stream_name"),
+ "app": d.Get("app_name"),
+ "check_level": utils.ValueIgnoreEmpty(d.Get("check_level")),
+ "start_time": utils.ValueIgnoreEmpty(d.Get("start_time")),
+ }
+}
+
+func resourceUrlAuthenticationCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ var (
+ cfg = meta.(*config.Config)
+ region = cfg.GetRegion(d)
+ httpUrl = "v1/{project_id}/auth/chain"
+ )
+
+ client, err := cfg.NewServiceClient("live", region)
+ if err != nil {
+ return diag.Errorf("error creating Live client: %s", err)
+ }
+
+ createPath := client.Endpoint + httpUrl
+ createPath = strings.ReplaceAll(createPath, "{project_id}", client.ProjectID)
+
+ createOpt := golangsdk.RequestOpts{
+ KeepResponseBody: true,
+ JSONBody: utils.RemoveNil(buildSignUrlBodyParams(d)),
+ }
+
+ resp, err := client.Request("POST", createPath, &createOpt)
+ if err != nil {
+ return diag.Errorf("error creating sign URL: %s", err)
+ }
+
+ resourceId, err := uuid.GenerateUUID()
+ if err != nil {
+ return diag.Errorf("unable to generate ID: %s", err)
+ }
+
+ d.SetId(resourceId)
+
+ respBody, err := utils.FlattenResponse(resp)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ keyChain := utils.PathSearch("keychain", respBody, make([]interface{}, 0)).([]interface{})
+ if len(keyChain) == 0 {
+ return diag.Errorf("err creating sign URL, not found 'key_chain' in API response")
+ }
+
+ var mErr *multierror.Error
+ mErr = multierror.Append(
+ mErr,
+ d.Set("key_chain", keyChain),
+ )
+
+ return diag.FromErr(mErr.ErrorOrNil())
+}
+
+func resourceUrlAuthenticationRead(_ context.Context, _ *schema.ResourceData, _ interface{}) diag.Diagnostics {
+ // This resource is only a one-time action resource for generating a signer URL
+ return nil
+}
+
+func resourceUrlAuthenticationDelete(_ context.Context, _ *schema.ResourceData, _ interface{}) diag.Diagnostics {
+ // This resource is only a one-time action resource for generating a signer URL
+ return nil
+}