From 9e30a68a611ce9112b941bb2730417de4a16a89a Mon Sep 17 00:00:00 2001 From: Elim Tsiagbey Date: Wed, 13 Dec 2023 16:05:01 -0500 Subject: [PATCH] feat(oidc): OIDC Prompt Support (#519) * feat(oidc): OIDC Prompt Support Boundary OIDC method does not currently support passing in prompts during authentication. This change adds the capability of passing OIDC prompts. Prompts are optional OIDC parameters that determine the behaviour of the authentication server: https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest Add new optional `prompts` option to `boundary_auth_method_oidc` resource. `prompts` option are optional OIDC parameters that determine the behavior of the authentication server. --- CHANGELOG.md | 7 +++++ docs/resources/auth_method_oidc.md | 25 +++++++++++++++ .../boundary_auth_method_oidc/resource.tf | 24 ++++++++++++++ .../provider/resource_auth_method_oidc.go | 31 +++++++++++++++++++ .../resource_auth_method_oidc_test.go | 4 +++ 5 files changed, 91 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b4c6a5a..50baf19a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ Canonical reference for changes, improvements, and bugfixes for the Boundary Terraform provider. +## Next + +### New and Improved + +* Add support for OIDC prompts. Using prompts, the Relying Party (RP) can customize the authentication and authorization flow to suit their specific needs and improve the user experience. [OIDC Authentication request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest) server. + ([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/519)) + ## 1.1.10 (October 11, 2023) ### New and Improved diff --git a/docs/resources/auth_method_oidc.md b/docs/resources/auth_method_oidc.md index a740edda..5e3d95d3 100644 --- a/docs/resources/auth_method_oidc.md +++ b/docs/resources/auth_method_oidc.md @@ -31,6 +31,30 @@ resource "boundary_auth_method_oidc" "vault" { signing_algorithms = ["RS256"] type = "oidc" } + +resource "boundary_auth_method_oidc" "oidc_with_one_prompt" { + api_url_prefix = "https://XO-XO-XO-XO-XOXOXO.boundary.hashicorp.cloud:9200" + client_id = "eieio" + client_secret = "hvo_secret_XO" + description = "My Boundary OIDC Auth Method With Prompt" + issuer = "https://sts.windows.net/TENANT_ID/" + scope_id = "global" + signing_algorithms = ["RS256"] + prompts = ["select_account"] + type = "oidc" +} + +resource "boundary_auth_method_oidc" "oidc_with_multiple_prompts" { + api_url_prefix = "https://XO-XO-XO-XO-XOXOXO.boundary.hashicorp.cloud:9200" + client_id = "eieio" + client_secret = "hvo_secret_XO" + description = "My Boundary OIDC Auth Method With Multiple Prompts" + issuer = "https://sts.windows.net/TENANT_ID/" + scope_id = "global" + signing_algorithms = ["RS256"] + prompts = ["consent", "select_account"] + type = "oidc" +} ``` @@ -57,6 +81,7 @@ resource "boundary_auth_method_oidc" "vault" { - `issuer` (String) The issuer corresponding to the provider, which must match the issuer field in generated tokens. - `max_age` (Number) The max age to provide to the provider, indicating how much time is allowed to have passed since the last authentication before the user is challenged again. A value of 0 sets an immediate requirement for all users to reauthenticate, and an unset maxAge results in a Terraform value of -1 and the default TTL of the chosen OIDC will be used. - `name` (String) The auth method name. Defaults to the resource name. +- `prompts` (List of String) The prompts passed to the identity provider to determine whether to prompt the end-user for reauthentication, account selection or consent. Please note the values passed are case-sensitive. The valid values are: `none`, `login`, `consent` and `select_account`. - `signing_algorithms` (List of String) Allowed signing algorithms for the provider's issued tokens. - `state` (String) Can be one of 'inactive', 'active-private', or 'active-public'. Currently automatically set to active-public. - `type` (String) The type of auth method; hardcoded. diff --git a/examples/resources/boundary_auth_method_oidc/resource.tf b/examples/resources/boundary_auth_method_oidc/resource.tf index 9c337cf9..ddd0227d 100644 --- a/examples/resources/boundary_auth_method_oidc/resource.tf +++ b/examples/resources/boundary_auth_method_oidc/resource.tf @@ -16,3 +16,27 @@ resource "boundary_auth_method_oidc" "vault" { signing_algorithms = ["RS256"] type = "oidc" } + +resource "boundary_auth_method_oidc" "oidc_with_one_prompt" { + api_url_prefix = "https://XO-XO-XO-XO-XOXOXO.boundary.hashicorp.cloud:9200" + client_id = "eieio" + client_secret = "hvo_secret_XO" + description = "My Boundary OIDC Auth Method With Prompt" + issuer = "https://sts.windows.net/TENANT_ID/" + scope_id = "global" + signing_algorithms = ["RS256"] + prompts = ["select_account"] + type = "oidc" +} + +resource "boundary_auth_method_oidc" "oidc_with_multiple_prompts" { + api_url_prefix = "https://XO-XO-XO-XO-XOXOXO.boundary.hashicorp.cloud:9200" + client_id = "eieio" + client_secret = "hvo_secret_XO" + description = "My Boundary OIDC Auth Method With Multiple Prompts" + issuer = "https://sts.windows.net/TENANT_ID/" + scope_id = "global" + signing_algorithms = ["RS256"] + prompts = ["consent", "select_account"] + type = "oidc" +} diff --git a/internal/provider/resource_auth_method_oidc.go b/internal/provider/resource_auth_method_oidc.go index 090dd817..cbc9b90d 100644 --- a/internal/provider/resource_auth_method_oidc.go +++ b/internal/provider/resource_auth_method_oidc.go @@ -29,6 +29,7 @@ const ( authmethodOidcIsPrimaryAuthMethodForScope = "is_primary_for_scope" authmethodOidcAccountClaimMapsKey = "account_claim_maps" authmethodOidcClaimsScopesKey = "claims_scopes" + authmethodOidcPromptsKey = "prompts" // computed-only parameters authmethodOidcCallbackUrlKey = "callback_url" @@ -147,6 +148,15 @@ func resourceAuthMethodOidc() *schema.Resource { }, Optional: true, }, + authmethodOidcPromptsKey: { + Description: "The prompts passed to the identity provider to determine whether to prompt the end-user for reauthentication, account selection or consent. " + + "Please note the values passed are case-sensitive. The valid values are: `none`, `login`, `consent` and `select_account`. ", + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Optional: true, + }, // OIDC specific immutable and computed parameters authmethodOidcClientSecretHmacKey: { @@ -239,6 +249,10 @@ func setFromOidcAuthMethodResponseMap(d *schema.ResourceData, raw map[string]int if p, ok := attrs[authmethodOidcClaimsScopesKey]; ok { d.Set(authmethodOidcClaimsScopesKey, p.([]interface{})) } + + if val, ok := attrs[authmethodOidcPromptsKey]; ok { + d.Set(authmethodOidcPromptsKey, val.([]interface{})) + } } d.SetId(raw["id"].(string)) @@ -321,6 +335,14 @@ func resourceAuthMethodOidcCreate(ctx context.Context, d *schema.ResourceData, m opts = append(opts, authmethods.WithOidcAuthMethodClaimsScopes(cList)) } + if prompts, ok := d.GetOk(authmethodOidcPromptsKey); ok { + promptList := []string{} + for _, p := range prompts.([]interface{}) { + promptList = append(promptList, p.(string)) + } + opts = append(opts, authmethods.WithOidcAuthMethodPrompts(promptList)) + } + nameVal, ok := d.GetOk(NameKey) if ok { nameStr := nameVal.(string) @@ -536,6 +558,15 @@ func resourceAuthMethodOidcUpdate(ctx context.Context, d *schema.ResourceData, m opts = append(opts, authmethods.WithOidcAuthMethodClaimsScopes(claimsScopes)) } } + if d.HasChange(authmethodOidcPromptsKey) { + if prompts, ok := d.GetOk(authmethodOidcPromptsKey); ok { + var promptsList []string + for _, prompt := range prompts.([]interface{}) { + promptsList = append(promptsList, prompt.(string)) + } + opts = append(opts, authmethods.WithOidcAuthMethodPrompts(promptsList)) + } + } if len(opts) > 0 { opts = append(opts, authmethods.WithAutomaticVersioning(true)) diff --git a/internal/provider/resource_auth_method_oidc_test.go b/internal/provider/resource_auth_method_oidc_test.go index fc5ffb16..c2022c29 100644 --- a/internal/provider/resource_auth_method_oidc_test.go +++ b/internal/provider/resource_auth_method_oidc_test.go @@ -67,6 +67,7 @@ EOT signing_algorithms = ["ES256"] account_claim_maps = ["oid=sub"] claims_scopes = ["profile"] + prompts = ["select_account"] }` fooAuthMethodOidcUpdate = ` @@ -90,6 +91,7 @@ EOT signing_algorithms = ["ES256"] account_claim_maps = ["oid=sub"] claims_scopes = ["profile"] + prompts = ["consent", "select_account"] // we need to disable this validation, since the updated issuer isn't discoverable disable_discovered_config_validation = true @@ -125,6 +127,7 @@ func TestAccAuthMethodOidc(t *testing.T) { testAccCheckAuthMethodAttrAryValueSet(provider, "boundary_auth_method_oidc.foo", authmethodOidcSigningAlgorithmsKey, []string{"ES256"}), testAccCheckAuthMethodAttrAryValueSet(provider, "boundary_auth_method_oidc.foo", authmethodOidcAccountClaimMapsKey, []string{"oid=sub"}), testAccCheckAuthMethodAttrAryValueSet(provider, "boundary_auth_method_oidc.foo", authmethodOidcClaimsScopesKey, []string{"profile"}), + testAccCheckAuthMethodAttrAryValueSet(provider, "boundary_auth_method_oidc.foo", authmethodOidcPromptsKey, []string{"select_account"}), testAccCheckAuthMethodResourceExists(provider, "boundary_auth_method_oidc.foo"), testAccIsPrimaryForScope(provider, "boundary_auth_method_oidc.foo", false), ), @@ -141,6 +144,7 @@ func TestAccAuthMethodOidc(t *testing.T) { resource.TestCheckResourceAttr("boundary_auth_method_oidc.foo", authmethodOidcMaxAgeKey, "-1"), testAccCheckAuthMethodAttrAryValueSet(provider, "boundary_auth_method_oidc.foo", authmethodOidcIdpCaCertsKey, []string{fooAuthMethodOidcCaCerts}), testAccCheckAuthMethodAttrAryValueSet(provider, "boundary_auth_method_oidc.foo", authmethodOidcAllowedAudiencesKey, []string{"foo_aud_update"}), + testAccCheckAuthMethodAttrAryValueSet(provider, "boundary_auth_method_oidc.foo", authmethodOidcPromptsKey, []string{"consent", "select_account"}), testAccCheckAuthMethodResourceExists(provider, "boundary_auth_method_oidc.foo"), testAccIsPrimaryForScope(provider, "boundary_auth_method_oidc.foo", true), testAccCheckAuthMethodResourceExists(provider, "boundary_auth_method_oidc.foo"),