Skip to content
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
13 changes: 8 additions & 5 deletions docs/auth0_apps_session-transfer_update.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,21 @@ auth0 apps session-transfer update [flags]
auth0 apps session-transfer update
auth0 apps session-transfer update <app-id>
auth0 apps session-transfer update <app-id> --can-create-token --json
auth0 apps session-transfer update <app-id> --delegation-allow-delegated-access=true --delegation-enforce-device-binding=asn
auth0 apps session-transfer update <app-id> --can-create-token=true --allowed-auth-methods=cookie,query --enforce-device-binding=ip
```


## Flags

```
-m, --allowed-auth-methods strings Comma-separated list of authentication methods (e.g., cookie, query).
-t, --can-create-token Allow creation of session transfer tokens.
-e, --enforce-device-binding string Device binding enforcement: 'none', 'ip', or 'asn'.
--json Output in json format.
--json-compact Output in compact json format.
-m, --allowed-auth-methods strings Comma-separated list of authentication methods (e.g., cookie, query).
-t, --can-create-token Allow creation of session transfer tokens.
--delegation-allow-delegated-access (Early Access) Allow the application to accept Session Transfer Tokens containing an Actor, enabling delegated (impersonation) access. Defaults to false.
--delegation-enforce-device-binding string (Early Access) Device binding enforcement for delegated (impersonation) access: 'ip' or 'asn'. Defaults to 'ip'.
-e, --enforce-device-binding string Device binding enforcement: 'none', 'ip', or 'asn'.
--json Output in json format.
--json-compact Output in compact json format.
```


Expand Down
51 changes: 47 additions & 4 deletions internal/cli/apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,18 @@ var (
Help: "Device binding enforcement: 'none', 'ip', or 'asn'.",
AlwaysPrompt: true,
}
appSTDelegationAllowAccess = Flag{
Name: "Allow Delegated Access",
LongForm: "delegation-allow-delegated-access",
Help: "(Early Access) Allow the application to accept Session Transfer Tokens containing an Actor, " +
"enabling delegated (impersonation) access. Defaults to false.",
}
appSTDelegationDeviceBinding = Flag{
Name: "Delegation Enforce Device Binding",
LongForm: "delegation-enforce-device-binding",
Help: "(Early Access) Device binding enforcement for delegated (impersonation) access: 'ip' or 'asn'. " +
"Defaults to 'ip'.",
}
refreshToken = Flag{
Name: "Refresh Token",
LongForm: "refresh-token",
Expand Down Expand Up @@ -1211,10 +1223,12 @@ func appsSessionTransferShowCmd(cli *cli) *cobra.Command {

func appsSessionTransferUpdateCmd(cli *cli) *cobra.Command {
var inputs struct {
ID string
CanCreateToken bool
AllowedAuthMethods []string
EnforceDeviceBinding string
ID string
CanCreateToken bool
AllowedAuthMethods []string
EnforceDeviceBinding string
DelegationAllowAccess bool
DelegationDeviceBinding string
}

cmd := &cobra.Command{
Expand All @@ -1224,6 +1238,7 @@ func appsSessionTransferUpdateCmd(cli *cli) *cobra.Command {
Example: ` auth0 apps session-transfer update
auth0 apps session-transfer update <app-id>
auth0 apps session-transfer update <app-id> --can-create-token --json
auth0 apps session-transfer update <app-id> --delegation-allow-delegated-access=true --delegation-enforce-device-binding=asn
auth0 apps session-transfer update <app-id> --can-create-token=true --allowed-auth-methods=cookie,query --enforce-device-binding=ip`,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
Expand All @@ -1235,6 +1250,16 @@ func appsSessionTransferUpdateCmd(cli *cli) *cobra.Command {
inputs.ID = args[0]
}

// Validate the delegation device binding before any API call.
if appSTDelegationDeviceBinding.IsSet(cmd) &&
inputs.DelegationDeviceBinding != "ip" && inputs.DelegationDeviceBinding != "asn" {
return fmt.Errorf(
"invalid value %q for --%s: must be one of 'ip' or 'asn'",
inputs.DelegationDeviceBinding,
appSTDelegationDeviceBinding.LongForm,
)
}

var (
current *management.Client
st management.SessionTransfer
Expand Down Expand Up @@ -1283,6 +1308,22 @@ func appsSessionTransferUpdateCmd(cli *cli) *cobra.Command {
st.EnforceDeviceBinding = current.SessionTransfer.EnforceDeviceBinding
}

// Delegation (EA) is sent only when a flag is set, leaving it untouched for
// others. The API merges sub-fields, so sending just the changed one is enough.
if appSTDelegationAllowAccess.IsSet(cmd) || appSTDelegationDeviceBinding.IsSet(cmd) {
delegation := &management.SessionTransferDelegation{}

if appSTDelegationAllowAccess.IsSet(cmd) {
delegation.AllowDelegatedAccess = &inputs.DelegationAllowAccess
}

if appSTDelegationDeviceBinding.IsSet(cmd) {
delegation.EnforceDeviceBinding = &inputs.DelegationDeviceBinding
}

st.Delegation = delegation
}

// Send update request.
clientST := &management.Client{SessionTransfer: &st}
if err := ansi.Waiting(func() error {
Expand All @@ -1302,6 +1343,8 @@ func appsSessionTransferUpdateCmd(cli *cli) *cobra.Command {
appSTCanCreateToken.RegisterBoolU(cmd, &inputs.CanCreateToken, false)
appSTAllowedAuthMethods.RegisterStringSliceU(cmd, &inputs.AllowedAuthMethods, nil)
appSTEnforceDeviceBinding.RegisterStringU(cmd, &inputs.EnforceDeviceBinding, "")
appSTDelegationAllowAccess.RegisterBoolU(cmd, &inputs.DelegationAllowAccess, false)
appSTDelegationDeviceBinding.RegisterStringU(cmd, &inputs.DelegationDeviceBinding, "")

return cmd
}
26 changes: 24 additions & 2 deletions internal/display/apps_session_transfer.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ type SessionTransferView struct {
AllowedMethods string
DeviceBinding string

// Delegation (EA) fields, shown only when hasDelegation is true.
hasDelegation bool
DelegationAllowAccess string
DelegationDeviceBinding string

raw interface{}
}

Expand All @@ -29,12 +34,21 @@ func (v *SessionTransferView) AsTableRow() []string {
}

func (v *SessionTransferView) KeyValues() [][]string {
return [][]string{
keyValues := [][]string{
{"CLIENT ID", v.ID},
{"CAN CREATE TOKEN", v.CanCreateTOKEN},
{"ALLOWED METHODS", v.AllowedMethods},
{"DEVICE BINDING", v.DeviceBinding},
}

if v.hasDelegation {
keyValues = append(keyValues,
[]string{"ALLOW DELEGATED ACCESS", v.DelegationAllowAccess},
[]string{"DELEGATION DEVICE BINDING", v.DelegationDeviceBinding},
)
}

return keyValues
}

func (v *SessionTransferView) Object() interface{} {
Expand All @@ -54,11 +68,19 @@ func (r *Renderer) SessionTransferUpdate(client *management.Client, id string) {
}

func MakeSessionTransferView(client *management.Client) *SessionTransferView {
return &SessionTransferView{
view := &SessionTransferView{
ID: client.GetClientID(),
CanCreateTOKEN: boolean(client.SessionTransfer.GetCanCreateSessionTransferToken()),
AllowedMethods: stringSliceToCommaSeparatedString(client.SessionTransfer.GetAllowedAuthenticationMethods()),
DeviceBinding: client.SessionTransfer.GetEnforceDeviceBinding(),
raw: client.SessionTransfer,
}

if delegation := client.GetSessionTransfer().GetDelegation(); delegation != nil {
view.hasDelegation = true
view.DelegationAllowAccess = boolean(delegation.GetAllowDelegatedAccess())
view.DelegationDeviceBinding = delegation.GetEnforceDeviceBinding()
}

return view
}
67 changes: 67 additions & 0 deletions internal/display/apps_session_transfer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package display

import (
"testing"

"github.com/auth0/go-auth0"
"github.com/auth0/go-auth0/management"
"github.com/stretchr/testify/assert"
)

func TestMakeSessionTransferView_WithoutDelegation(t *testing.T) {
client := &management.Client{
ClientID: auth0.String("client-id"),
SessionTransfer: &management.SessionTransfer{
CanCreateSessionTransferToken: auth0.Bool(true),
AllowedAuthenticationMethods: &[]string{"cookie", "query"},
EnforceDeviceBinding: auth0.String("ip"),
},
}

view := MakeSessionTransferView(client)

assert.False(t, view.hasDelegation)
assert.Equal(t, "", view.DelegationAllowAccess)
assert.Equal(t, "", view.DelegationDeviceBinding)

// Delegation rows must be omitted when no delegation is configured.
keyValues := view.KeyValues()
assert.Equal(t, [][]string{
{"CLIENT ID", "client-id"},
{"CAN CREATE TOKEN", boolean(true)},
{"ALLOWED METHODS", "cookie, query"},
{"DEVICE BINDING", "ip"},
}, keyValues)
}

func TestMakeSessionTransferView_WithDelegation(t *testing.T) {
client := &management.Client{
ClientID: auth0.String("client-id"),
SessionTransfer: &management.SessionTransfer{
CanCreateSessionTransferToken: auth0.Bool(true),
AllowedAuthenticationMethods: &[]string{"cookie"},
EnforceDeviceBinding: auth0.String("ip"),
Delegation: &management.SessionTransferDelegation{
AllowDelegatedAccess: auth0.Bool(true),
EnforceDeviceBinding: auth0.String("asn"),
},
},
}

view := MakeSessionTransferView(client)

assert.True(t, view.hasDelegation)
assert.Equal(t, boolean(true), view.DelegationAllowAccess)
assert.Equal(t, "asn", view.DelegationDeviceBinding)

// Delegation rows must be appended after the base session-transfer rows.
keyValues := view.KeyValues()
assert.Equal(t, [][]string{
{"CLIENT ID", "client-id"},
{"CAN CREATE TOKEN", boolean(true)},
{"ALLOWED METHODS", "cookie"},
{"DEVICE BINDING", "ip"},
{"ALLOW DELEGATED ACCESS", boolean(true)},
{"DELEGATION DEVICE BINDING", "asn"},
}, keyValues)
}
36 changes: 23 additions & 13 deletions test/integration/apps-test-cases.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,17 @@ tests:
- ALLOWED METHODS cookie, query
- DEVICE BINDING ip

042 - it successfully creates a m2m app with allow-any-profile-of-type and outputs in json:
# Delegation is an Early Access sub-feature gated behind the
# `cte_session_transfer_token` tenant flag. So this case only covers the client-side validation, which
# runs before any API call and is therefore safe on any tenant.
042 - it rejects an invalid delegation device binding value:
command: auth0 apps session-transfer update $(./test/integration/scripts/get-app-id.sh) --delegation-enforce-device-binding=none --json
exit-code: 1
stderr:
contains:
- "must be one of 'ip' or 'asn'"

043 - it successfully creates a m2m app with allow-any-profile-of-type and outputs in json:
command: auth0 apps create --name integration-test-app-te1 --type m2m --description TEApp1 --allow-any-profile-of-type custom_authentication --json
exit-code: 0
stdout:
Expand All @@ -327,29 +337,29 @@ tests:
app_type: non_interactive
token_exchange.allow_any_profile_of_type: "[custom_authentication]"

043 - it successfully updates token exchange types on an app and outputs in json:
044 - it successfully updates token exchange types on an app and outputs in json:
command: auth0 apps update $(./test/integration/scripts/get-app-id.sh) --allow-any-profile-of-type custom_authentication --json
exit-code: 0
stdout:
json:
token_exchange.allow_any_profile_of_type: "[custom_authentication]"

044 - it successfully shows token exchange types on an app:
045 - it successfully shows token exchange types on an app:
command: auth0 apps show $(./test/integration/scripts/get-app-id.sh)
exit-code: 0
stdout:
contains:
- TOKEN EXCHANGE TYPES custom_authentication

045 - given a test app, it successfully deletes the app:
046 - given a test app, it successfully deletes the app:
command: auth0 apps delete $(./test/integration/scripts/get-app-id.sh) --force
exit-code: 0

046 - it successfully creates a resource server app with resource-server-identifier:
047 - it successfully creates a resource server app with resource-server-identifier:
command: ./test/integration/scripts/get-resource-server-app-id.sh
exit-code: 0

047 - it successfully creates a resource server app with resource-server-identifier and outputs in json:
048 - it successfully creates a resource server app with resource-server-identifier and outputs in json:
command: auth0 apps show $(./test/integration/scripts/get-resource-server-app-id.sh) --json
exit-code: 0
stdout:
Expand All @@ -358,30 +368,30 @@ tests:
app_type: resource_server
resource_server_identifier: http://integration-test-api-newapi

048 - it shows resource server app with resource-server-identifier:
049 - it shows resource server app with resource-server-identifier:
command: auth0 apps show $(./test/integration/scripts/get-resource-server-app-id.sh)
exit-code: 0
stdout:
contains:
- TYPE Resource Server
- RESOURCE SERVER IDENTIFIER http://integration-test-api-newapi

049 - it fails to update resource-server-identifier (immutable property):
050 - it fails to update resource-server-identifier (immutable property):
command: auth0 apps update $(./test/integration/scripts/get-resource-server-app-id.sh) --resource-server-identifier http://new-api.localhost --json
exit-code: 1
stderr:
contains:
- "Unknown flag: --resource-server-identifier"

050 - given a resource server app, it successfully deletes the app:
051 - 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 third-party app with strict security mode:
052 - it successfully creates a third-party app with strict security mode:
command: ./test/integration/scripts/get-3p-app-id.sh
exit-code: 0

052 - it successfully shows a third-party app with security mode and redirection policy in json:
053 - it successfully shows a third-party app with security mode and redirection policy in json:
command: auth0 apps show $(./test/integration/scripts/get-3p-app-id.sh) --json
exit-code: 0
stdout:
Expand All @@ -392,13 +402,13 @@ tests:
third_party_security_mode: strict
redirection_policy: open_redirect_protection

053 - it successfully updates the redirection-policy of a third-party app and outputs in json:
054 - it successfully updates the redirection-policy of a third-party app and outputs in json:
command: auth0 apps update $(./test/integration/scripts/get-3p-app-id.sh) --redirection-policy allow_always --json
exit-code: 0
stdout:
json:
redirection_policy: allow_always

054 - given a third-party app, it successfully deletes the app:
055 - given a third-party app, it successfully deletes the app:
command: auth0 apps delete $(./test/integration/scripts/get-3p-app-id.sh) --force
exit-code: 0
Loading