From d7af64895a2d9a4cc719e3d1c38a34560166f8f3 Mon Sep 17 00:00:00 2001 From: KIRAN KUMAR B Date: Mon, 18 May 2026 17:49:55 +0530 Subject: [PATCH 1/5] feat(apps): add support for third-party security mode and redirection policy - Introduced new flags for third-party applications: - `third-party-security-mode` to specify 'strict' or 'permissive'. - `redirection-policy` to control Auth0's behavior on authentication errors. - Updated `createAppCmd` and `updateAppCmd` functions to handle new inputs. - Enhanced `applicationView` to display third-party security mode and redirection policy. - Ensured backward compatibility by integrating new features without affecting existing functionality. --- internal/cli/apps.go | 84 ++++++++++++++++++++++++++++++---------- internal/display/apps.go | 12 ++++++ 2 files changed, 75 insertions(+), 21 deletions(-) diff --git a/internal/cli/apps.go b/internal/cli/apps.go index f7ffc716c..3b7f108dc 100644 --- a/internal/cli/apps.go +++ b/internal/cli/apps.go @@ -176,6 +176,18 @@ var ( ShortForm: "p", Help: "Comma-separated list of enabled token exchange types for this client. Possible values: custom_authentication, on_behalf_of_token_exchange.", } + appThirdPartySecurityMode = Flag{ + Name: "Third Party Security Mode", + LongForm: "third-party-security-mode", + ShortForm: "s", + Help: "Security mode for third-party clients: 'strict' or 'permissive'.", + } + appRedirectionPolicy = Flag{ + Name: "Redirection Policy", + LongForm: "redirection-policy", + ShortForm: "y", + Help: "Controls whether Auth0 redirects users to the application's callback URL on authentication errors or in email verification flows: 'allow_always' or 'open_redirect_protection'.", + } ) func appsCmd(cli *cli) *cobra.Command { @@ -434,6 +446,8 @@ func createAppCmd(cli *cli) *cobra.Command { RefreshToken string ResourceServerIdentifier string AllowAnyProfileOfType []string + ThirdPartySecurityMode string + RedirectionPolicy string } var oidcConformant = true var algorithm = "RS256" @@ -456,7 +470,8 @@ func createAppCmd(cli *cli) *cobra.Command { auth0 apps create -n myapp -d -t [native|spa|regular|m2m|resource_server] -r --json --metadata "foo=bar" --metadata "bazz=buzz" auth0 apps create -n myapp -d -t [native|spa|regular|m2m|resource_server] -r --json --metadata "foo=bar,bazz=buzz" auth0 apps create --name "My API Client" --type resource_server --resource-server-identifier "https://api.example.com" - auth0 apps create --name myapp --type resource_server --allow-any-profile-of-type custom_authentication,on_behalf_of_token_exchange`, + auth0 apps create --name myapp --type resource_server --allow-any-profile-of-type custom_authentication,on_behalf_of_token_exchange + auth0 apps create --name "My 3P App" --type regular --third-party-security-mode strict --redirection-policy open_redirect_protection`, RunE: func(cmd *cobra.Command, args []string) error { if err := appName.Ask(cmd, &inputs.Name, nil); err != nil { return err @@ -474,6 +489,7 @@ func createAppCmd(cli *cli) *cobra.Command { appIsNative := apiTypeFor(inputs.Type) == appTypeNative appIsSPA := apiTypeFor(inputs.Type) == appTypeSPA appIsResourceServer := apiTypeFor(inputs.Type) == appTypeResourceServer + isThirdPartyApp := inputs.ThirdPartySecurityMode != "" || inputs.RedirectionPolicy != "" // Prompt for callback URLs if app is not m2m and not resource_server. if !appIsM2M && !appIsResourceServer { @@ -488,8 +504,8 @@ func createAppCmd(cli *cli) *cobra.Command { } } - // Prompt for logout URLs if app is not m2m and not resource_server. - if !appIsM2M && !appIsResourceServer { + // Prompt for logout URLs if app is not m2m and not resource_server and not a third-party app. + if !appIsM2M && !appIsResourceServer && !isThirdPartyApp { var defaultValue string if !appIsNative { @@ -593,11 +609,21 @@ func createAppCmd(cli *cli) *cobra.Command { a.TokenEndpointAuthMethod = apiAuthMethodFor(inputs.AuthMethod) } + if inputs.ThirdPartySecurityMode != "" { + a.ThirdPartySecurityMode = &inputs.ThirdPartySecurityMode + } + if inputs.RedirectionPolicy != "" { + a.RedirectionPolicy = &inputs.RedirectionPolicy + } + if isThirdPartyApp { + a.IsFirstParty = auth0.Bool(false) + } + // Set grants. - if len(inputs.Grants) == 0 { - a.GrantTypes = apiDefaultGrantsFor(inputs.Type) - } else { + if len(inputs.Grants) > 0 { a.GrantTypes = apiGrantsFor(inputs.Grants) + } else if !isThirdPartyApp { + a.GrantTypes = apiDefaultGrantsFor(inputs.Type) } // Create app. @@ -633,26 +659,30 @@ func createAppCmd(cli *cli) *cobra.Command { appTEAllowAnyProfileOfType.RegisterStringSlice(cmd, &inputs.AllowAnyProfileOfType, nil) revealSecrets.RegisterBool(cmd, &inputs.RevealSecrets, false) refreshToken.RegisterString(cmd, &inputs.RefreshToken, "") + appThirdPartySecurityMode.RegisterString(cmd, &inputs.ThirdPartySecurityMode, "") + appRedirectionPolicy.RegisterString(cmd, &inputs.RedirectionPolicy, "") return cmd } func updateAppCmd(cli *cli) *cobra.Command { var inputs struct { - ID string - Name string - Type string - Description string - Callbacks []string - AllowedOrigins []string - AllowedWebOrigins []string - AllowedLogoutURLs []string - AuthMethod string - Grants []string - RevealSecrets bool - Metadata map[string]string - RefreshToken string - AllowAnyProfileOfType []string + ID string + Name string + Type string + Description string + Callbacks []string + AllowedOrigins []string + AllowedWebOrigins []string + AllowedLogoutURLs []string + AuthMethod string + Grants []string + RevealSecrets bool + Metadata map[string]string + RefreshToken string + AllowAnyProfileOfType []string + ThirdPartySecurityMode string + RedirectionPolicy string } cmd := &cobra.Command{ @@ -673,7 +703,9 @@ func updateAppCmd(cli *cli) *cobra.Command { auth0 apps update -n myapp -d -t [native|spa|regular|m2m] -r --json --metadata "foo=bar" auth0 apps update -n myapp -d -t [native|spa|regular|m2m] -r --json --metadata "foo=bar" --metadata "bazz=buzz" auth0 apps update -n myapp -d -t [native|spa|regular|m2m] -r --json --metadata "foo=bar,bazz=buzz" - auth0 apps update --allow-any-profile-of-type custom_authentication,on_behalf_of_token_exchange`, + auth0 apps update --allow-any-profile-of-type custom_authentication,on_behalf_of_token_exchange + auth0 apps update --redirection-policy allow_always + auth0 apps update --third-party-security-mode strict --redirection-policy open_redirect_protection`, RunE: func(cmd *cobra.Command, args []string) error { var current *management.Client @@ -847,6 +879,14 @@ func updateAppCmd(cli *cli) *cobra.Command { } } + if appThirdPartySecurityMode.IsSet(cmd) { + a.ThirdPartySecurityMode = &inputs.ThirdPartySecurityMode + } + + if appRedirectionPolicy.IsSet(cmd) { + a.RedirectionPolicy = &inputs.RedirectionPolicy + } + if err := ansi.Waiting(func() error { return cli.api.Client.Update(cmd.Context(), inputs.ID, a) }); err != nil { @@ -874,6 +914,8 @@ func updateAppCmd(cli *cli) *cobra.Command { appTEAllowAnyProfileOfType.RegisterStringSliceU(cmd, &inputs.AllowAnyProfileOfType, nil) revealSecrets.RegisterBool(cmd, &inputs.RevealSecrets, false) refreshToken.RegisterString(cmd, &inputs.RefreshToken, "") + appThirdPartySecurityMode.RegisterStringU(cmd, &inputs.ThirdPartySecurityMode, "") + appRedirectionPolicy.RegisterStringU(cmd, &inputs.RedirectionPolicy, "") return cmd } diff --git a/internal/display/apps.go b/internal/display/apps.go index 853468834..bbcf7c1dd 100644 --- a/internal/display/apps.go +++ b/internal/display/apps.go @@ -40,6 +40,8 @@ type applicationView struct { RefreshToken string ResourceServerIdentifier string AllowAnyProfileOfType []string + ThirdPartySecurityMode string + RedirectionPolicy string revealSecret bool raw interface{} @@ -128,6 +130,14 @@ func (v *applicationView) KeyValues() [][]string { keyValues = append(keyValues, []string{"TOKEN EXCHANGE TYPES", strings.Join(v.AllowAnyProfileOfType, ", ")}) } + if v.ThirdPartySecurityMode != "" { + keyValues = append(keyValues, []string{"THIRD PARTY SECURITY MODE", v.ThirdPartySecurityMode}) + } + + if v.RedirectionPolicy != "" { + keyValues = append(keyValues, []string{"REDIRECTION POLICY", v.RedirectionPolicy}) + } + return keyValues } @@ -213,6 +223,8 @@ func makeApplicationView(client *management.Client, revealSecrets bool) *applica Metadata: mapPointerToArray(client.ClientMetadata), ResourceServerIdentifier: client.GetResourceServerIdentifier(), AllowAnyProfileOfType: client.GetTokenExchange().GetAllowAnyProfileOfType(), + ThirdPartySecurityMode: client.GetThirdPartySecurityMode(), + RedirectionPolicy: client.GetRedirectionPolicy(), raw: client, RefreshToken: string(jsonRefreshToken), } From cd30773bb757a6ec44f90d0483807a4c869b6217 Mon Sep 17 00:00:00 2001 From: KIRAN KUMAR B Date: Mon, 18 May 2026 17:59:18 +0530 Subject: [PATCH 2/5] feat(cli): enhance client grant resource fetching with default_for support - Updated the FetchData method in clientGrantResourceFetcher to handle grants marked as default_for, allowing for more accurate resource naming. - Introduced a new test case to validate the handling of default_for grants in TestClientGrantResourceFetcher_FetchData, ensuring expected behavior when fetching client grants with different audiences. --- internal/cli/terraform_fetcher.go | 8 +++- internal/cli/terraform_fetcher_test.go | 61 ++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/internal/cli/terraform_fetcher.go b/internal/cli/terraform_fetcher.go index d0106905c..d3df097c5 100644 --- a/internal/cli/terraform_fetcher.go +++ b/internal/cli/terraform_fetcher.go @@ -274,8 +274,14 @@ func (f *clientGrantResourceFetcher) FetchData(ctx context.Context) (importDataL } for _, grant := range grants.ClientGrants { + var resourceName string + if grant.GetDefaultFor() != "" { + resourceName = "auth0_client_grant." + sanitizeResourceName("default_for_"+grant.GetDefaultFor()+"_"+grant.GetAudience()) + } else { + resourceName = "auth0_client_grant." + sanitizeResourceName(grant.GetClientID()+"_"+grant.GetAudience()) + } data = append(data, importDataItem{ - ResourceName: "auth0_client_grant." + sanitizeResourceName(grant.GetClientID()+"_"+grant.GetAudience()), + ResourceName: resourceName, ImportID: grant.GetID(), }) } diff --git a/internal/cli/terraform_fetcher_test.go b/internal/cli/terraform_fetcher_test.go index 4cf7c7d72..b733dd1be 100644 --- a/internal/cli/terraform_fetcher_test.go +++ b/internal/cli/terraform_fetcher_test.go @@ -723,6 +723,67 @@ func TestClientGrantResourceFetcher_FetchData(t *testing.T) { _, err := fetcher.FetchData(context.Background()) assert.EqualError(t, err, "failed to list clients") }) + + t.Run("it handles default_for grants correctly", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + clientGrantAPI := mock.NewMockClientGrantAPI(ctrl) + clientGrantAPI.EXPECT(). + List(gomock.Any(), gomock.Any()). + Return( + &management.ClientGrantList{ + List: management.List{ + Start: 0, + Limit: 3, + Total: 3, + }, + ClientGrants: []*management.ClientGrant{ + { + ID: auth0.String("cgr_1"), + ClientID: auth0.String("client-id-1"), + Audience: auth0.String("https://travel0.com/api"), + }, + { + ID: auth0.String("cgr_2"), + DefaultFor: auth0.String("third_party_clients"), + Audience: auth0.String("https://travel0.com/api"), + }, + { + ID: auth0.String("cgr_3"), + DefaultFor: auth0.String("third_party_clients"), + Audience: auth0.String("https://partner-api.example.com"), + }, + }, + }, + nil, + ) + + fetcher := clientGrantResourceFetcher{ + api: &auth0.API{ + ClientGrant: clientGrantAPI, + }, + } + + expectedData := importDataList{ + { + ResourceName: "auth0_client_grant.client_id_1_https_travel0_com_api", + ImportID: "cgr_1", + }, + { + ResourceName: "auth0_client_grant.default_for_third_party_clients_https_travel0_com_api", + ImportID: "cgr_2", + }, + { + ResourceName: "auth0_client_grant.default_for_third_party_clients_https_partner_api_example_com", + ImportID: "cgr_3", + }, + } + + data, err := fetcher.FetchData(context.Background()) + assert.NoError(t, err) + assert.Equal(t, expectedData, data) + }) } func TestConnectionResourceFetcher_FetchData(t *testing.T) { From 2622fe47797662317c89072384fd9bec935335f7 Mon Sep 17 00:00:00 2001 From: KIRAN KUMAR B Date: Mon, 18 May 2026 18:00:32 +0530 Subject: [PATCH 3/5] feat(docs): update application creation and update examples with new flags - Added examples for `auth0 apps create` and `auth0 apps update` commands to demonstrate the usage of `--third-party-security-mode` and `--redirection-policy` flags. - Updated documentation to reflect the new security features for third-party applications, enhancing clarity for users. --- docs/auth0_apps_create.md | 3 +++ docs/auth0_apps_update.md | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/docs/auth0_apps_create.md b/docs/auth0_apps_create.md index 011038122..a6cb7ed37 100644 --- a/docs/auth0_apps_create.md +++ b/docs/auth0_apps_create.md @@ -31,6 +31,7 @@ auth0 apps create [flags] auth0 apps create -n myapp -d -t [native|spa|regular|m2m|resource_server] -r --json --metadata "foo=bar,bazz=buzz" auth0 apps create --name "My API Client" --type resource_server --resource-server-identifier "https://api.example.com" auth0 apps create --name myapp --type resource_server --allow-any-profile-of-type custom_authentication,on_behalf_of_token_exchange + auth0 apps create --name "My 3P App" --type regular --third-party-security-mode strict --redirection-policy open_redirect_protection ``` @@ -48,9 +49,11 @@ auth0 apps create [flags] --metadata stringToString Arbitrary keys-value pairs (max 255 characters each), that can be assigned to each application. More about application metadata: https://auth0.com/docs/get-started/applications/configure-application-metadata (default []) -n, --name string Name of the application. -o, --origins strings Comma-separated list of URLs allowed to make requests from JavaScript to Auth0 API (typically used with CORS). By default, all your callback URLs will be allowed. This field allows you to enter other origins if necessary. You can also use wildcards at the subdomain level (e.g., https://*.contoso.com). Query strings and hash information are not taken into account when validating these URLs. + -y, --redirection-policy string Controls whether Auth0 redirects users to the application's callback URL on authentication errors or in email verification flows: 'allow_always' or 'open_redirect_protection'. -z, --refresh-token string Refresh Token Config for the application, formatted as JSON. --resource-server-identifier string The identifier of the resource server that this client is associated with. This property can only be sent when app_type=resource_server and cannot be changed once the client is created. -r, --reveal-secrets Display the application secrets ('signing_keys', 'client_secret') as part of the command output. + -s, --third-party-security-mode string Security mode for third-party clients: 'strict' or 'permissive'. -t, --type string Type of application: - native: mobile, desktop, CLI and smart device apps running natively. - spa (single page application): a JavaScript front-end app that uses an API. diff --git a/docs/auth0_apps_update.md b/docs/auth0_apps_update.md index b9b5fdd23..5429c8606 100644 --- a/docs/auth0_apps_update.md +++ b/docs/auth0_apps_update.md @@ -30,6 +30,8 @@ auth0 apps update [flags] auth0 apps update -n myapp -d -t [native|spa|regular|m2m] -r --json --metadata "foo=bar" --metadata "bazz=buzz" auth0 apps update -n myapp -d -t [native|spa|regular|m2m] -r --json --metadata "foo=bar,bazz=buzz" auth0 apps update --allow-any-profile-of-type custom_authentication,on_behalf_of_token_exchange + auth0 apps update --redirection-policy allow_always + auth0 apps update --third-party-security-mode strict --redirection-policy open_redirect_protection ``` @@ -47,8 +49,10 @@ auth0 apps update [flags] --metadata stringToString Arbitrary keys-value pairs (max 255 characters each), that can be assigned to each application. More about application metadata: https://auth0.com/docs/get-started/applications/configure-application-metadata (default []) -n, --name string Name of the application. -o, --origins strings Comma-separated list of URLs allowed to make requests from JavaScript to Auth0 API (typically used with CORS). By default, all your callback URLs will be allowed. This field allows you to enter other origins if necessary. You can also use wildcards at the subdomain level (e.g., https://*.contoso.com). Query strings and hash information are not taken into account when validating these URLs. + -y, --redirection-policy string Controls whether Auth0 redirects users to the application's callback URL on authentication errors or in email verification flows: 'allow_always' or 'open_redirect_protection'. -z, --refresh-token string Refresh Token Config for the application, formatted as JSON. -r, --reveal-secrets Display the application secrets ('signing_keys', 'client_secret') as part of the command output. + -s, --third-party-security-mode string Security mode for third-party clients: 'strict' or 'permissive'. -t, --type string Type of application: - native: mobile, desktop, CLI and smart device apps running natively. - spa (single page application): a JavaScript front-end app that uses an API. From 3fa9de2e35a146bac87b59e351f5d17ccbb5c7e6 Mon Sep 17 00:00:00 2001 From: KIRAN KUMAR B Date: Mon, 18 May 2026 18:03:33 +0530 Subject: [PATCH 4/5] test(apps): add test case for creating regular app with third-party security mode - Added a new test case to validate the creation of a regular app with third-party security mode set to strict and redirection policy set to open_redirect_protection. - The test ensures that the app is created successfully and outputs the expected JSON response. --- test/integration/apps-test-cases.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/integration/apps-test-cases.yaml b/test/integration/apps-test-cases.yaml index cc88d9c0b..c79912521 100644 --- a/test/integration/apps-test-cases.yaml +++ b/test/integration/apps-test-cases.yaml @@ -376,3 +376,13 @@ tests: 050 - given a resource server app, it successfully deletes the app: command: auth0 apps delete $(./test/integration/scripts/get-resource-server-app-id.sh) --force exit-code: 0 + + 051 - it successfully creates a regular app with third-party-security-mode and redirection-policy and outputs in json: + command: auth0 apps create --name integration-test-app-3p-strict --type regular --description 3PApp1 --third-party-security-mode strict --redirection-policy open_redirect_protection --json + exit-code: 0 + stdout: + json: + name: integration-test-app-3p-strict + app_type: regular_web + third_party_security_mode: strict + redirection_policy: open_redirect_protection From a44b7bb72c59ef19bb587f51365b39bf7198f0c8 Mon Sep 17 00:00:00 2001 From: KIRAN KUMAR B Date: Mon, 18 May 2026 18:12:03 +0530 Subject: [PATCH 5/5] chore(terraform): lint fix --- internal/cli/terraform_fetcher_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/cli/terraform_fetcher_test.go b/internal/cli/terraform_fetcher_test.go index b733dd1be..e7fc03f92 100644 --- a/internal/cli/terraform_fetcher_test.go +++ b/internal/cli/terraform_fetcher_test.go @@ -745,14 +745,14 @@ func TestClientGrantResourceFetcher_FetchData(t *testing.T) { Audience: auth0.String("https://travel0.com/api"), }, { - ID: auth0.String("cgr_2"), + ID: auth0.String("cgr_2"), DefaultFor: auth0.String("third_party_clients"), - Audience: auth0.String("https://travel0.com/api"), + Audience: auth0.String("https://travel0.com/api"), }, { - ID: auth0.String("cgr_3"), + ID: auth0.String("cgr_3"), DefaultFor: auth0.String("third_party_clients"), - Audience: auth0.String("https://partner-api.example.com"), + Audience: auth0.String("https://partner-api.example.com"), }, }, },