# saif-resources / external-identity

Resolves Okta (external tenant) OAuth/OIDC app settings for a consuming service.

### Native Okta lookups, zero remote state (#845)

This module has **no** `tfe_outputs` reads. All `okta_app` fields — including
the front-end/Web app's `OpenIdConnectClientId`/`OpenIdConnectClientSecret`,
previously a residual remote-state read — are now native Okta data-source
lookups:

- **API app** (`data.okta_apps.api_app` + `data.okta_app_oauth.api_app`) —
  label derived from `project_id` (`lower(project_id)` prod,
  `lower("${project_id}-np")` non-prod), mirroring the producer module's own
  naming. Resolved in two steps: search with `q`, filter the results down to
  an exact label match, then look the app up by `id`. A single
  `okta_app_oauth { label = ... }` lookup is **not** safe here — see
  [Label collisions](#label-collisions) below.
- **Front-end/Web app** (`data.okta_apps.front_end` +
  `data.okta_app_oauth.front_end`) — only a minority of consuming services
  provision one, so a direct `okta_app_oauth` lookup (which hard-fails on no
  match) can't gate on existence. The plural `data.okta_apps` data source
  fixes that: it returns an empty list (no error) when its exact `label`
  match finds nothing, giving a genuine existence signal. Its `project_id` is
  derived from this module's `project_id` by replacing the `-api-<type>-`
  segment with `-web-` (e.g. `pol-api-exp-policyportal` →
  `pol-web-policyportal` — confirmed against every real front-end pairing in
  production). See the comments on `data "okta_apps" "front_end"` in
  `main.tf` for the full rationale.

### Label collisions

The singular `okta_app_oauth` data source's `label` argument is unsafe when
any other app's label starts with the one being looked up. The provider
implements it as a `limit=1` query against Okta's List Apps API, where `q` is
a case-insensitive `startsWith` match over `label` and `name` and results are
sorted by creation date. If a prefix-overlapping app was created first, it
takes the single slot and the lookup fails with:

```text
Error: no OAuth application found with the provided label: <label>
```

even though the requested app exists and is ACTIVE. `clm-api-proc-claim` hit
this against `clm-api-proc-claimintake`. The failure depends on app creation
order in the org, not on configuration, so it can appear on a workspace that
previously planned cleanly.

This is tracked upstream as
[okta/terraform-provider-okta#2847](https://github.com/okta/terraform-provider-okta/issues/2847).
The generic `okta_app` data source was fixed for the same bug in #1111/#1115,
but the fix was never propagated to `okta_app_oauth` or `okta_app_saml`, and
it is still unfixed as of provider 6.11.0. Both app lookups in this module
therefore go through the plural `okta_apps` data source, which paginates the
full result set, and filter it for an exact label match in a local.

A `lifecycle.precondition` asserts that exactly one app matched. This is load
bearing, not cosmetic: with zero matches the `id` argument would be `null`,
which makes the provider list applications with no search filter at all and
bind whichever app comes back first. That app's `client_secret` would then be
written to Key Vault under this service's name and its `client_id` published
as `OAuthClientId_ext`.

Coverage lives in `tests/validate.tftest.hcl`.

<!-- BEGIN_TF_DOCS -->
## Providers

| Name | Version |
|------|---------|
| <a name="provider_azurerm.shared-services"></a> [azurerm.shared-services](#provider\_azurerm.shared-services) | >= 4.0, < 5.0 |
| <a name="provider_okta"></a> [okta](#provider\_okta) | >= 4.18.0, < 5.0.0 |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_context"></a> [context](#input\_context) | Platform context from the environment module | `any` | n/a | yes |
| <a name="input_enable_oidc"></a> [enable\_oidc](#input\_enable\_oidc) | Whether to emit OpenIdConnectClientId\_ext / OpenIdConnectClientSecret\_ext app settings | `bool` | `false` | no |
| <a name="input_identity"></a> [identity](#input\_identity) | Identity bundle from the identity module (needs uami\_principal\_id for KV RBAC) | `any` | n/a | yes |
| <a name="input_project_id"></a> [project\_id](#input\_project\_id) | The project identifier, used for KV secret naming | `string` | n/a | yes |

## Outputs

| Name | Description |
|------|-------------|
| <a name="output_app_settings"></a> [app\_settings](#output\_app\_settings) | Map of Okta (ext) credential app settings. Merge into webapp app\_settings. |
| <a name="output_auth_server_audience"></a> [auth\_server\_audience](#output\_auth\_server\_audience) | The Okta authorization server audience (for APIM policies) |
| <a name="output_auth_server_url"></a> [auth\_server\_url](#output\_auth\_server\_url) | The Okta authorization server URL (for APIM policies) |
| <a name="output_has_open_id_connect"></a> [has\_open\_id\_connect](#output\_has\_open\_id\_connect) | Whether the Okta workspace has OpenID Connect configured |
| <a name="output_token_lifetime"></a> [token\_lifetime](#output\_token\_lifetime) | Token lifetime configuration from Okta data module. Contains ExternalAccess, ExternalRefresh, CorpAccess, CorpRefresh keys. |

## Resources


- resource.azurerm_key_vault_secret.oidc_client_secret (/terraform-docs/modules/external-identity/main.tf#190)
- resource.azurerm_key_vault_secret.okta_client_secret (/terraform-docs/modules/external-identity/main.tf#180)
- resource.azurerm_role_assignment.oidc_secret_reader (/terraform-docs/modules/external-identity/main.tf#213)
- resource.azurerm_role_assignment.okta_secret_reader (/terraform-docs/modules/external-identity/main.tf#200)
- data source.okta_app_oauth.api_app (/terraform-docs/modules/external-identity/main.tf#65)
- data source.okta_app_oauth.front_end (/terraform-docs/modules/external-identity/main.tf#116)
- data source.okta_apps.api_app (/terraform-docs/modules/external-identity/main.tf#60)
- data source.okta_apps.front_end (/terraform-docs/modules/external-identity/main.tf#111)
- data source.okta_auth_server.api_app (/terraform-docs/modules/external-identity/main.tf#81)
<!-- END_TF_DOCS -->

---

[View source on GitHub](https://github.com/saif-corp/forge/blob/main/src/terraform/saif-resources/modules/external-identity/README.md)
