Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add OIDC backchannel initiators #1045

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
11 changes: 11 additions & 0 deletions docs/data-sources/client.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ data "auth0_client" "some-client-by-id" {
- `native_social_login` (List of Object) Configuration settings to toggle native social login for mobile native applications. Once this is set it must stay set, with both resources set to `false` in order to change the `app_type`. (see [below for nested schema](#nestedatt--native_social_login))
- `oidc_backchannel_logout_urls` (Set of String) Set of URLs that are valid to call back from Auth0 for OIDC backchannel logout. Currently only one URL is allowed.
- `oidc_conformant` (Boolean) Indicates whether this client will conform to strict OIDC specifications.
- `oidc_logout` (List of Object) Configure OIDC logout for the Client (see [below for nested schema](#nestedatt--oidc_logout))
- `organization_require_behavior` (String) Defines how to proceed during an authentication transaction when `organization_usage = "require"`. Can be `no_prompt` (default), `pre_login_prompt` or `post_login_prompt`.
- `organization_usage` (String) Defines how to proceed during an authentication transaction with regards to an organization. Can be `deny` (default), `allow` or `require`.
- `refresh_token` (List of Object) Configuration settings for the refresh tokens issued for this client. (see [below for nested schema](#nestedatt--refresh_token))
Expand Down Expand Up @@ -554,6 +555,16 @@ Read-Only:



<a id="nestedatt--oidc_logout"></a>
### Nested Schema for `oidc_logout`

Read-Only:

- `backchannel_logout_initiators_mode` (String)
- `backchannel_logout_selected_initiators` (Set of String)
- `backchannel_logout_urls` (Set of String)


<a id="nestedatt--refresh_token"></a>
### Nested Schema for `refresh_token`

Expand Down
16 changes: 15 additions & 1 deletion docs/resources/client.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,9 @@ resource "auth0_client" "my_client" {
- `logo_uri` (String) URL of the logo for the client. Recommended size is 150px x 150px. If none is set, the default badge for the application type will be shown.
- `mobile` (Block List, Max: 1) Additional configuration for native mobile apps. (see [below for nested schema](#nestedblock--mobile))
- `native_social_login` (Block List, Max: 1) Configuration settings to toggle native social login for mobile native applications. Once this is set it must stay set, with both resources set to `false` in order to change the `app_type`. (see [below for nested schema](#nestedblock--native_social_login))
- `oidc_backchannel_logout_urls` (Set of String) Set of URLs that are valid to call back from Auth0 for OIDC backchannel logout. Currently only one URL is allowed.
- `oidc_backchannel_logout_urls` (Set of String, Deprecated) Set of URLs that are valid to call back from Auth0 for OIDC backchannel logout. Currently only one URL is allowed.
- `oidc_conformant` (Boolean) Indicates whether this client will conform to strict OIDC specifications.
- `oidc_logout` (Block List, Max: 1) Configure OIDC logout for the Client (see [below for nested schema](#nestedblock--oidc_logout))
- `organization_require_behavior` (String) Defines how to proceed during an authentication transaction when `organization_usage = "require"`. Can be `no_prompt` (default), `pre_login_prompt` or `post_login_prompt`.
- `organization_usage` (String) Defines how to proceed during an authentication transaction with regards to an organization. Can be `deny` (default), `allow` or `require`.
- `refresh_token` (Block List, Max: 1) Configuration settings for the refresh tokens issued for this client. (see [below for nested schema](#nestedblock--refresh_token))
Expand Down Expand Up @@ -527,6 +528,19 @@ Optional:



<a id="nestedblock--oidc_logout"></a>
### Nested Schema for `oidc_logout`

Required:

- `backchannel_logout_initiators_mode` (String) Determines the configuration method for enabling initiators. `custom` enables only the initiators listed in the backchannel_logout_selected_initiators set, `all` enables all current and future initiators.
- `backchannel_logout_urls` (Set of String) Set of URLs that are valid to call back from Auth0 for OIDC backchannel logout. Currently only one URL is allowed.

Optional:

- `backchannel_logout_selected_initiators` (Set of String) Contains the list of initiators to be enabled for the given client.


<a id="nestedblock--refresh_token"></a>
### Nested Schema for `refresh_token`

Expand Down
23 changes: 22 additions & 1 deletion internal/auth0/client/expand.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package client

import (
"fmt"

"github.com/auth0/go-auth0"
"github.com/auth0/go-auth0/management"
"github.com/hashicorp/go-cty/cty"
Expand Down Expand Up @@ -42,6 +41,7 @@ func expandClient(data *schema.ResourceData) (*management.Client, error) {
EncryptionKey: value.MapOfStrings(config.GetAttr("encryption_key")),
IsTokenEndpointIPHeaderTrusted: value.Bool(config.GetAttr("is_token_endpoint_ip_header_trusted")),
OIDCBackchannelLogout: expandOIDCBackchannelLogout(data),
Copy link
Contributor

@developerkunal developerkunal Oct 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps it would be better to create a nested OIDCLogout structure instead of the current approach, as oidc_backchannel_logout_urls already contains other values. Instead of removing oidc_backchannel_logout_urls, we can deprecate it and introduce OIDCLogout, with nested properties such as oidc_backchannel_logout_urls and BackChannelLogoutInitiators, similar to the implementation in the SDK. This approach ensures backward compatibility, as it's not going to be a major release.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The concern I had with that approach is you introduce the potential of setting the back channel logout urls in two different places, via the depreciated property and via the new nested properties. I imagine the Auth0 API will reject if you try that but I wasn't sure?

I also was trying to follow the proposal in this issue #1030
Generally I do prefer flat json to nested properties.

It seems the main reason for this change is your comment about backwards compatibility. These properties exist on the API and are set even though you can't control them via terraform currently. I guess the concern is someone gets a provider update and then it will try to set them back to the default values if they don't add their current values to their terraform? I can see that would be an issue. I just hate resorting to nested JSON but I suppose there isn't a better option.

OIDCLogout: expandOIDCLogout(data),
ClientMetadata: expandClientMetadata(data),
RefreshToken: expandClientRefreshToken(data),
JWTConfiguration: expandClientJWTConfiguration(data),
Expand Down Expand Up @@ -149,6 +149,27 @@ func expandOIDCBackchannelLogout(data *schema.ResourceData) *management.OIDCBack
}
}

func expandOIDCLogout(data *schema.ResourceData) *management.OIDCLogout {
oidcLogoutConfig := data.GetRawConfig().GetAttr("oidc_logout")
if oidcLogoutConfig.IsNull() {
return nil
}

var oidcLogout management.OIDCLogout

oidcLogoutConfig.ForEachElement(func(_ cty.Value, config cty.Value) (stop bool) {
oidcLogout.BackChannelLogoutURLs = value.Strings(config.GetAttr("backchannel_logout_urls"))
oidcLogout.BackChannelLogoutInitiators = &management.BackChannelLogoutInitiators{
Mode: value.String(config.GetAttr("backchannel_logout_initiators_mode")),
SelectedInitiators: value.Strings(config.GetAttr("backchannel_logout_selected_initiators")),
}

return stop
})

return &oidcLogout
}

func expandClientRefreshToken(data *schema.ResourceData) *management.ClientRefreshToken {
refreshTokenConfig := data.GetRawConfig().GetAttr("refresh_token")
if refreshTokenConfig.IsNull() {
Expand Down
32 changes: 30 additions & 2 deletions internal/auth0/client/flatten.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package client
import (
"context"
"fmt"

"github.com/auth0/go-auth0/management"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
Expand Down Expand Up @@ -63,6 +62,34 @@ func flattenClientRefreshTokenConfiguration(refreshToken *management.ClientRefre
}
}

func flattenOIDCBackchannelURLs(backchannelLogout *management.OIDCBackchannelLogout, logout *management.OIDCLogout) []string {
if logout != nil {
return nil
} else {
return backchannelLogout.GetBackChannelLogoutURLs()
}
}

func flattenOIDCLogout(oidcLogout *management.OIDCLogout) []interface{} {
if oidcLogout == nil {
return nil
}

flattened := map[string]interface{}{
"backchannel_logout_urls": oidcLogout.GetBackChannelLogoutURLs(),
"backchannel_logout_initiators_mode": oidcLogout.GetBackChannelLogoutInitiators().GetMode(),
}

selectedInitiators := oidcLogout.GetBackChannelLogoutInitiators().GetSelectedInitiators()
if len(selectedInitiators) > 0 {
flattened["backchannel_logout_selected_initiators"] = selectedInitiators
}

return []interface{}{
flattened,
}
}

func flattenClientMobile(mobile *management.ClientMobile) []interface{} {
if mobile == nil {
return nil
Expand Down Expand Up @@ -557,7 +584,8 @@ func flattenClient(data *schema.ResourceData, client *management.Client) error {
data.Set("initiate_login_uri", client.GetInitiateLoginURI()),
data.Set("signing_keys", client.SigningKeys),
data.Set("client_metadata", client.GetClientMetadata()),
data.Set("oidc_backchannel_logout_urls", client.GetOIDCBackchannelLogout().GetBackChannelLogoutURLs()),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here don't remove this just deprecate this.

data.Set("oidc_backchannel_logout_urls", flattenOIDCBackchannelURLs(client.GetOIDCBackchannelLogout(), client.GetOIDCLogout())),
data.Set("oidc_logout", flattenOIDCLogout(client.GetOIDCLogout())),
data.Set("require_pushed_authorization_requests", client.GetRequirePushedAuthorizationRequests()),
data.Set("default_organization", flattenDefaultOrganization(client.GetDefaultOrganization())),
data.Set("require_proof_of_possession", client.GetRequireProofOfPossession()),
Expand Down
33 changes: 33 additions & 0 deletions internal/auth0/client/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ func NewResource() *schema.Resource {
},
Optional: true,
Description: "Set of URLs that are valid to call back from Auth0 for OIDC backchannel logout. Currently only one URL is allowed.",
Deprecated: "Configure oidc_logout block instead to gain access to all properties.",
},
"grant_types": {
Type: schema.TypeList,
Expand Down Expand Up @@ -1322,6 +1323,38 @@ func NewResource() *schema.Resource {
Optional: true,
Description: "Makes the use of Proof-of-Possession mandatory for this client.",
},
"oidc_logout": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Description: "Configure OIDC logout for the Client",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"backchannel_logout_urls": {
Type: schema.TypeSet,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Required: true,
Description: "Set of URLs that are valid to call back from Auth0 for OIDC backchannel logout. Currently only one URL is allowed.",
},
"backchannel_logout_initiators_mode": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{"all", "custom"}, false),
Description: "Determines the configuration method for enabling initiators. `custom` enables only the initiators listed in the backchannel_logout_selected_initiators set, `all` enables all current and future initiators.",
},
"backchannel_logout_selected_initiators": {
Type: schema.TypeSet,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Optional: true,
Description: "Contains the list of initiators to be enabled for the given client.",
},
},
},
},
},
}
}
Expand Down
69 changes: 69 additions & 0 deletions internal/auth0/client/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2427,3 +2427,72 @@ func TestAccClientWithDefaultOrganization(t *testing.T) {
},
})
}

const testAccCreateClientWithOIDCLogout = `
resource "auth0_client" "my_client" {
name = "Acceptance Test - OIDC Logout - {{.testName}}"
app_type = "spa"

oidc_logout {
backchannel_logout_urls = ["https://auth0.test/all/logout"]
backchannel_logout_initiators_mode = "all"
}
}
`

const testAccUpdateClientWithOIDCLogout = `
resource "auth0_client" "my_client" {
name = "Acceptance Test - OIDC Logout - {{.testName}}"
app_type = "spa"

oidc_logout {
backchannel_logout_urls = ["https://auth0.test/custom/logout"]
backchannel_logout_initiators_mode = "custom"
backchannel_logout_selected_initiators = ["rp-logout", "idp-logout", "password-changed", "session-expired"]
}
}
`

const testAccUpdateClientWithOIDCLogoutWhenRemovedFromConfig = `
resource "auth0_client" "my_client" {
name = "Acceptance Test - OIDC Logout - {{.testName}}"
app_type = "spa"
}
`

func TestAccClientOIDCLogout(t *testing.T) {
acctest.Test(t, resource.TestCase{
Steps: []resource.TestStep{
{
Config: acctest.ParseTestName(testAccCreateClientWithOIDCLogout, t.Name()),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("auth0_client.my_client", "name", fmt.Sprintf("Acceptance Test - OIDC Logout - %s", t.Name())),
resource.TestCheckResourceAttr("auth0_client.my_client", "app_type", "spa"),
resource.TestCheckResourceAttr("auth0_client.my_client", "oidc_logout.#", "1"),
resource.TestCheckResourceAttr("auth0_client.my_client", "oidc_logout.0.backchannel_logout_urls.#", "1"),
resource.TestCheckResourceAttr("auth0_client.my_client", "oidc_logout.0.backchannel_logout_initiators_mode", "all"),
resource.TestCheckResourceAttr("auth0_client.my_client", "oidc_logout.0.backchannel_logout_selected_initiators.#", "0"),
),
},
{
Config: acctest.ParseTestName(testAccUpdateClientWithOIDCLogout, t.Name()),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("auth0_client.my_client", "name", fmt.Sprintf("Acceptance Test - OIDC Logout - %s", t.Name())),
resource.TestCheckResourceAttr("auth0_client.my_client", "app_type", "spa"),
resource.TestCheckResourceAttr("auth0_client.my_client", "oidc_logout.#", "1"),
resource.TestCheckResourceAttr("auth0_client.my_client", "oidc_logout.0.backchannel_logout_urls.#", "1"),
resource.TestCheckResourceAttr("auth0_client.my_client", "oidc_logout.0.backchannel_logout_initiators_mode", "custom"),
resource.TestCheckResourceAttr("auth0_client.my_client", "oidc_logout.0.backchannel_logout_selected_initiators.#", "4"),
),
},
{
Config: acctest.ParseTestName(testAccUpdateClientWithOIDCLogoutWhenRemovedFromConfig, t.Name()),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("auth0_client.my_client", "name", fmt.Sprintf("Acceptance Test - OIDC Logout - %s", t.Name())),
resource.TestCheckResourceAttr("auth0_client.my_client", "app_type", "spa"),
resource.TestCheckResourceAttr("auth0_client.my_client", "oidc_logout.#", "0"),
),
},
},
})
}