Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion docs/resources/oauth2_client.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,21 @@ OAuth2 clients are used for machine-to-machine authentication or user-facing OAu

~> **Important:** The `client_secret` is only returned when the client is first created. Store it securely immediately after creation. It cannot be retrieved later, including after `terraform import`.

## Custom Client ID

By default, a random `client_id` is generated when the client is created. You can specify a custom `client_id` for consistency across environments:

```hcl
resource "ory_oauth2_client" "api" {
client_id = "my-api-client"
client_name = "API Client"
grant_types = ["client_credentials"]
scope = "read write"
}
```

~> **Note:** Changing the `client_id` after creation forces the resource to be destroyed and recreated. The `client_id` must be unique within the project.

## Example Usage

```terraform
Expand Down Expand Up @@ -141,6 +156,14 @@ resource "ory_oauth2_client" "cli_tool" {
scope = "openid offline_access"
}

# Client with a custom client_id (useful for consistency across environments)
resource "ory_oauth2_client" "custom_id" {
client_id = "my-api-client"
client_name = "API Client with Custom ID"
grant_types = ["client_credentials"]
scope = "api:read api:write"
}

# Same-apply: Create project and OAuth2 client together
# Use resource-level credentials when the project doesn't exist yet
resource "ory_oauth2_client" "same_apply" {
Expand Down Expand Up @@ -329,6 +352,7 @@ terraform import ory_oauth2_client.api <client-id>
- `backchannel_logout_session_required` (Boolean) Whether the client requires a session identifier in back-channel logout notifications.
- `backchannel_logout_uri` (String) OpenID Connect back-channel logout URI.
- `client_credentials_grant_access_token_lifespan` (String) Access token lifespan for client credentials grant (e.g., '1h', '30m').
- `client_id` (String) The OAuth2 client ID. If not specified, a random ID will be generated. Once set, changing this value forces recreation of the resource.
- `client_uri` (String) URL of the client's homepage.
- `contacts` (List of String) List of contact email addresses for the client maintainers.
- `device_authorization_grant_access_token_lifespan` (String) Access token lifespan for device authorization grant (e.g., '1h').
Expand Down Expand Up @@ -364,6 +388,5 @@ terraform import ory_oauth2_client.api <client-id>

### Read-Only

- `client_id` (String) The OAuth2 client ID.
- `client_secret` (String, Sensitive) The OAuth2 client secret. Only returned on creation.
- `id` (String) Internal Terraform ID (same as client_id).
8 changes: 8 additions & 0 deletions examples/resources/ory_oauth2_client/resource.tf
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,14 @@ resource "ory_oauth2_client" "cli_tool" {
scope = "openid offline_access"
}

# Client with a custom client_id (useful for consistency across environments)
resource "ory_oauth2_client" "custom_id" {
client_id = "my-api-client"
client_name = "API Client with Custom ID"
grant_types = ["client_credentials"]
scope = "api:read api:write"
}

# Same-apply: Create project and OAuth2 client together
# Use resource-level credentials when the project doesn't exist yet
resource "ory_oauth2_client" "same_apply" {
Expand Down
16 changes: 15 additions & 1 deletion internal/resources/oauth2client/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,14 @@ resource "ory_oauth2_client" "api" {
token_endpoint_auth_method = "client_secret_post"
}

# With a custom client_id
resource "ory_oauth2_client" "custom" {
client_id = "my-api-client"
client_name = "API Client with Custom ID"
grant_types = ["client_credentials"]
scope = "read write"
}

output "client_id" {
value = ory_oauth2_client.api.client_id
}
Expand Down Expand Up @@ -149,10 +157,12 @@ func (r *OAuth2ClientResource) Schema(ctx context.Context, req resource.SchemaRe
},
},
"client_id": schema.StringAttribute{
Description: "The OAuth2 client ID.",
Description: "The OAuth2 client ID. If not specified, a random ID will be generated. Once set, changing this value forces recreation of the resource.",
Optional: true,
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
stringplanmodifier.RequiresReplace(),
},
},
Comment on lines 159 to 167
"client_secret": schema.StringAttribute{
Expand Down Expand Up @@ -407,6 +417,10 @@ func (r *OAuth2ClientResource) Create(ctx context.Context, req resource.CreateRe
Scope: ory.PtrString(plan.Scope.ValueString()),
}

if !plan.ClientID.IsNull() && !plan.ClientID.IsUnknown() {
oauthClient.ClientId = ory.PtrString(plan.ClientID.ValueString())
}

if !plan.GrantTypes.IsNull() && !plan.GrantTypes.IsUnknown() {
var grantTypes []string
resp.Diagnostics.Append(plan.GrantTypes.ElementsAs(ctx, &grantTypes, false)...)
Expand Down
47 changes: 47 additions & 0 deletions internal/resources/oauth2client/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
package oauth2client_test

import (
"fmt"
"testing"
"time"

"github.com/hashicorp/terraform-plugin-testing/helper/resource"

Expand Down Expand Up @@ -227,6 +229,51 @@ func TestAccOAuth2ClientResource_withResourceCredentials(t *testing.T) {
})
}

func TestAccOAuth2ClientResource_withCustomClientID(t *testing.T) {
clientID := fmt.Sprintf("tf-acc-test-%d", time.Now().UnixNano())

acctest.RunTest(t, resource.TestCase{
PreCheck: func() { acctest.AccPreCheck(t) },
ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories(),
Steps: []resource.TestStep{
// Create with custom client_id
{
Config: acctest.LoadTestConfig(t, "testdata/with_custom_client_id.tf.tmpl", map[string]string{
"Name": "Test Client Custom ID",
"ClientID": clientID,
}),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("ory_oauth2_client.test", "client_id", clientID),
resource.TestCheckResourceAttr("ory_oauth2_client.test", "id", clientID),
resource.TestCheckResourceAttr("ory_oauth2_client.test", "client_name", "Test Client Custom ID"),
resource.TestCheckResourceAttr("ory_oauth2_client.test", "scope", "api:read"),
resource.TestCheckResourceAttrSet("ory_oauth2_client.test", "client_secret"),
),
},
// ImportState
{
ResourceName: "ory_oauth2_client.test",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"client_secret"},
},
// Update (change name/scope, client_id stays the same)
{
Config: acctest.LoadTestConfig(t, "testdata/with_custom_client_id_updated.tf.tmpl", map[string]string{
"Name": "Test Client Custom ID Updated",
"ClientID": clientID,
}),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("ory_oauth2_client.test", "client_id", clientID),
resource.TestCheckResourceAttr("ory_oauth2_client.test", "id", clientID),
resource.TestCheckResourceAttr("ory_oauth2_client.test", "client_name", "Test Client Custom ID Updated"),
resource.TestCheckResourceAttr("ory_oauth2_client.test", "scope", "api:read api:write"),
),
},
},
})
}

func TestAccOAuth2ClientResource_withTokenLifespans(t *testing.T) {
acctest.RunTest(t, resource.TestCase{
PreCheck: func() { acctest.AccPreCheck(t) },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
resource "ory_oauth2_client" "test" {
client_id = "[[ .ClientID ]]"
client_name = "[[ .Name ]]"

grant_types = ["client_credentials"]
response_types = ["token"]
scope = "api:read"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
resource "ory_oauth2_client" "test" {
client_id = "[[ .ClientID ]]"
client_name = "[[ .Name ]]"

grant_types = ["client_credentials"]
response_types = ["token"]
scope = "api:read api:write"
}
15 changes: 15 additions & 0 deletions templates/resources/oauth2_client.md.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,21 @@ OAuth2 clients are used for machine-to-machine authentication or user-facing OAu

~> **Important:** The `client_secret` is only returned when the client is first created. Store it securely immediately after creation. It cannot be retrieved later, including after `terraform import`.

## Custom Client ID

By default, a random `client_id` is generated when the client is created. You can specify a custom `client_id` for consistency across environments:

```hcl
resource "ory_oauth2_client" "api" {
client_id = "my-api-client"
client_name = "API Client"
grant_types = ["client_credentials"]
scope = "read write"
}
```

~> **Note:** Changing the `client_id` after creation forces the resource to be destroyed and recreated. The `client_id` must be unique within the project.

## Example Usage

{{ tffile "examples/resources/ory_oauth2_client/resource.tf" }}
Expand Down