wso2 / wso2/wso2-cli

feat(apim): bootstrap registers the identity provider and public federated client, with iam creating the federation client

Open
#164 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

ready-for-human Type/Improvement
Dominant language
Go
Stars
0
Forks
3
Avg merge
8h 43m
Merged PRs (30d)
60

Description

Problem Statement

After wso2 iam connect and wso2 iam bootstrap, a developer who wants API Manager on the same login has to register, by hand, everything API Manager needs to federate to ThunderID: a confidential client on ThunderID for API Manager to sign in through, an identity provider on API Manager pointing at it with claim and role mappings and just-in-time provisioning, and a public PKCE client on API Manager with the shell's four loopback callbacks whose authentication step is that identity provider. docs/guides/one-login-thunder-apim.md section 3 lists the steps; they are console clicks or SOAP admin calls, each with a value that must match another (the resource parameter, the groups claim, enabled=true on the default authenticator), and a mistake in any one of them shows up later as auth.credential_unavailable, a second password prompt, or every command refused with auth.narrowing_unavailable. wso2 apim bootstrap today registers only a confidential client through dynamic client registration, which is right for a pipeline and wrong for a browser identity; its next line has to explain the difference. wso2 apim connect --client-id then needs an id the developer copies from the console. Measured in the 2026-09-07 exercise 4 tryout (cli-exercise-4/FINDINGS.md, remaining issue 2) and recorded in docs/research/2026-09-06-single-login-spikes.md section 1.

Solution

Two commands, one per product, because each product's secrets stay inside its own module and the shell brokers a session for one namespace at a time.

wso2 iam apps create apim-federation --type federation --for https://localhost:9443 creates on ThunderID the confidential client API Manager federates through: on the console authentication flow so its sign-on is the one the CLI's login establishes, redirect <for>/commonauth, grants authorization code and refresh, client_secret_post, ID token attributes and scope claims for email, groups and name. The secret is shown once, and the next line names the variable to export and the bootstrap to run.

WSO2_APIM_ADMIN_PASSWORD=… WSO2_APIM_FEDERATION_CLIENT_SECRET=… wso2 apim bootstrap --url https://localhost:9443 --login-provider http://localhost:8492 --federation-client-id apim-federation writes on API Manager, idempotently: the identity provider for that login provider (OpenID Connect federated authenticator, endpoints, callback, the scope and resource query parameters, claim mappings, the group-to-role mapping, just-in-time provisioning on), and the public client wso2-cli-sso (four loopback callbacks, public, PKCE S256 mandatory, JWT tokens, one federated authentication step through that identity provider, consent skipped). It prints wso2 apim connect https://localhost:9443 --client-id <public client id> as the next line. Without --login-provider it does what it does today, for pipelines.

The guide's section 3 becomes those two commands. The user never opens the API Manager console.

User Stories

  1. As a developer, I want wso2 iam apps create apim-federation --type federation --for https://localhost:9443 to create the confidential client API Manager needs on ThunderID, so that I do not build it in the console.
  2. As a developer, I want that client on the same authentication flow as the CLI's own client, so that one sign-on answers both.
  3. As a developer, I want the generated secret shown once in the result, and the next line to say export WSO2_APIM_FEDERATION_CLIENT_SECRET=<the secret above>; then wso2 apim bootstrap --url <for> --login-provider <issuer> --federation-client-id apim-federation, so that I know exactly what to run next without the secret appearing on that line.
  4. As a developer, I want running the same apps create --type federation again to report created false and change nothing, so that reruns are safe.
  5. As a developer, I want --type federation without --for refused with iam.missing_flag, so that the redirect is never guessed.
  6. As a developer, I want wso2 apim bootstrap --url <base> --login-provider <issuer> to create the identity provider on API Manager with the OpenID Connect federated authenticator, so that API Manager signs users in at ThunderID.
  7. As a developer, I want the identity provider named after the login provider's host unless --identity-provider <name> says otherwise, so that two deployments do not collide and I can adopt an existing name.
  8. As a developer, I want the authorization endpoint to be the login provider URL the browser reaches and the token and userinfo endpoints to be --login-provider-internal-url when given, so that API Manager in a container reaches ThunderID on the host.
  9. As a developer, I want the authenticator's additional query parameters to carry scope=openid email groups and resource=<base>/oauth2/token, so that ThunderID accepts the authorization and the ID token carries the groups claim.
  10. As a developer, I want the identity provider to map groups to the local role claim and email to the email claim, and to map a ThunderID group to an API Manager role with --map-group Administrators=admin (repeatable, that pair by default), so that a signed-in administrator gets the apim:* scopes.
  11. As a developer, I want just-in-time provisioning on, silent, to the primary user store, so that mapped roles reach the scope issuer.
  12. As a developer, I want an existing identity provider of that name updated in place with its default authenticator kept enabled, so that a rerun after a service provider references it does not fail with "Error in disabling default federated authenticator".
  13. As a developer, I want the public client wso2-cli-sso (name from --public-client-name) registered with the four loopback callbacks, made public, PKCE S256 mandatory, JWT token type, and its authentication set to one federated step through the identity provider with consent skipped, so that the shell's browser login works as a public client.
  14. As a developer, I want a public client that already exists to be reused and brought to that configuration, reported created false, so that reruns converge.
  15. As a developer, I want bootstrap's result to show the identity provider name, the public client id, and what was created versus already present, and its next line to be wso2 apim connect <base> --client-id <public client id>, so that the value connect needs is on screen.
  16. As a developer, I want the confidential pipeline client bootstrap registers today to still be registered on the same run and reported in its own row, so that a deployment set up once serves both people and pipelines.
  17. As a developer, I want a missing WSO2_APIM_FEDERATION_CLIENT_SECRET or --federation-client-id refused with apim.missing_flag naming the wso2 iam apps create --type federation line, so that the order of the two commands is discoverable.
  18. As a developer, I want API Manager's refusals surfaced with their body under apim.refused, so that a wrong admin password or a locked-down deployment is diagnosable.
  19. As a developer, I want the certificate refusal apim.certificate_untrusted from bootstrap when WSO2_CA_FILE is not set, so that the first failure names the fix.
  20. As a developer, I want wso2 apim bootstrap without --login-provider unchanged, so that pipelines keep their path.
  21. As a developer, I want the guide's section 3 replaced by the two commands and their outputs, so that the whole journey is CLI commands.
  22. As a pipeline author, I want none of this to require a browser, so that it can run from a setup job with the two passwords in the secret store.
  23. As a maintainer, I want every write to be read-before-write and idempotent, and no secret to appear in any next line or log, so that the module keeps its contract.

Implementation Decisions

  • Vocabulary: the ThunderID client is the federation client; the API Manager client the shell logs in with is the public client; the object on API Manager that points at ThunderID is the identity provider. These are API Manager's and ThunderID's own words; CONTEXT.md gains none.
  • iam module: apps create gains --type federation beside m2m and public, with --for <url> (the product it federates: redirect <for>/commonauth). It writes a custom application on the console flow family, client_secret_post, grants authorization_code and refresh_token, ID token user attributes email, groups, name, scope claims email and groups, a generated secret. The result row shows the secret once; the next line names the variable, never the value. Idempotent by client id.
  • apim module: bootstrap gains --login-provider <issuer>, --login-provider-internal-url <url> (default: the issuer), --federation-client-id <id>, --identity-provider <name> (default: the issuer host with the port, dots and colons replaced by hyphens, prefixed wso2-cli-), --public-client-name <name> (default wso2-cli-sso), --map-group <group>=<role> repeatable (default Administrators=admin). It reads WSO2_APIM_ADMIN_PASSWORD as today and WSO2_APIM_FEDERATION_CLIENT_SECRET.
  • API Manager writes go through the admin services the spike measured, with the administrator's basic authentication: dynamic client registration for the public client's creation; OAuthAdminService to make it public, PKCE mandatory, JWT; IdentityApplicationManagementService to set the federated authentication step; IdentityProviderMgtService to add or update the identity provider. Each is read first and written only when the read differs. The SOAP request and response shapes live in the apim module's internal client beside the existing JSON client; no new dependency.
  • The identity provider carries: the OpenID Connect federated authenticator with the federation client id and secret, authorization endpoint at the login provider, token and userinfo endpoints at the internal URL, callback <base>/commonauth, commonAuthQueryParams scope=openid email groups&resource=<base>/oauth2/token; claim mappings groups to the role claim and email to the email claim, sub as the user claim; role mappings from --map-group; JIT provisioning enabled, silent, PRIMARY; the default authenticator entry enabled=true on update.
  • The result gains rows: Identity provider <name> (created|present), Public client <id> (created|present), alongside today's confidential client row. The next line is the wso2 apim connect <base> --client-id <public client id> line; the pipeline connect line moves to a second sentence.
  • The apim descriptor is unchanged: the public client id stays per deployment and reaches the shell through connect --client-id.
  • Documentation: the guide's section 3 becomes the two commands; the commands reference rows for iam apps create and apim bootstrap; the module READMEs; the target experience document's bootstrap line; the spikes document gains one line saying which steps bootstrap now applies.

Testing Decisions

  • A good test drives the command through the module's handler with the fake deployment and asserts on the result rows, the problem code and recovery, and the requests the fake recorded, never on internal helpers.
  • iam module: modules/iam/cmd/wso2-module-iam/management_test.go fake ThunderID (prior art: TestAppsCreate…, TestRoleAssignResolvesNamesAndIsIdempotent) covers the federation client's body, the secret shown once and absent from the next line, idempotence, and the --for refusal.
  • apim module: modules/apim/cmd/wso2-module-apim/commands_test.go with fake_test.go (prior art: TestBootstrap…, TestKeyManagersAdd…) gains the four admin services as recorded XML or JSON exchanges from the spike; cases: fresh deployment creates all three, rerun creates none, existing identity provider updated with the enabled flag, missing secret and missing client id refusals, --map-group in the identity provider body, no --login-provider unchanged, certificate refusal.
  • Live: one run against cli-apim and cli-thunder3 from ~/dev/wso2/cli-exercise-4 under a fresh identity provider name and public client name, then apim connect, login, apis list; recorded as a row in the live matrix.

Out of Scope

  • Registering the key manager for the gateway; wso2 apim key-managers add exists.
  • Identity Server or Asgardeo as the login provider; the identity provider shape is OpenID Connect and the derivation is measured on ThunderID only.
  • Removing what bootstrap wrote; no unbootstrap.
  • The confidential pipeline client's shape; unchanged.
  • Any change to the shell, the descriptor or connect.

Further Notes

  • Why two commands: ThunderID's system permission and API Manager's administrator password belong to different modules, and the shell brokers one namespace per invocation. A single command would need the apim module to hold a ThunderID token, which ADR 0005 forbids. The one value that crosses is the federation client's secret, in an environment variable, the way every other secret reaches a module.
  • Why SOAP: API Manager 4.7.0 exposes no REST API for a service provider's authentication steps or for the identity provider's federated authenticator; the spike used OAuthAdminService, IdentityApplicationManagementService and IdentityProviderMgtService with basic authentication and they are what bootstrap applies.
  • Related: #163 (gateway record), the one-login guide section 3, docs/research/2026-09-06-single-login-spikes.md section 1, exercise 4 findings remaining issue 2.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the existing handlers and tests in modules/iam/cmd/wso2-module-iam/management_test.go and modules/apim/cmd/wso2-module-apim/commands_test.go, including fake_test.go and the existing TestAppsCreate… and TestBootstrap… cases. Read the module clients and the referenced guide and spike documents first. Done means both commands, idempotent API Manager and ThunderID setup, required refusal cases, recorded service exchanges, and the section 3 documentation are covered.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
api, cli, documentation
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.