Skip to content

Authentication: OIDC WIF Client

The OIDC WIF Client test checks that your security token service (STS) or OAuth 2.0 token endpoint handles OpenID Connect workload identity federation correctly. Federation is how systems such as Google Cloud, Microsoft Entra ID, AWS IAM and Keycloak let an external workload swap its platform-issued ID token for their own credentials, with no stored secret.

Helena plays the whole workload side:

  1. The identity provider. Each test has its own Helena-hosted OIDC issuer with a public discovery document and JWKS. You configure your target once to trust that issuer.
  2. The workload. Each run mints a signed ID token from that issuer, presents it to your target with the exchange flow you choose, and checks the response.

With token variants (expired, wrong audience, forged signature, alg: none, …), the same setup also checks that your target refuses tokens it must refuse.

sequenceDiagram
    participant H as Helena (workload)
    participant I as Helena issuer
    participant T as Your STS / token endpoint
    H->>H: Mint ID token (variant applied)
    H->>T: Token request (chosen flow)
    T->>I: GET /.well-known/openid-configuration
    T->>I: GET /jwks
    T->>T: Verify signature, iss, aud, exp, sub, conditions
    T-->>H: Token, or 4xx error
    H->>H: Assertions → Test Log

Sidebar: Authentication → OIDC WIF Client

Before you begin

  • You need administrative access to the target, so you can register Helena's issuer as a trusted identity provider.
  • The target must be able to fetch Helena's issuer URLs over HTTPS (https://app.helena.dev/oidc/wif/…). Some targets need an allow-list entry.
  • The token endpoint must be a public HTTPS URL. See How Helena reaches your target.

Set it up

  1. Create the test. Choose the Exchange flow for your target, then fill in the Token endpoint and Audience (aud). Leave Token variant as Valid token. Save.
  2. Copy the trust details. The test page has a Trust setup panel that shows:
    • Issuer URL: https://app.helena.dev/oidc/wif/{test-uuid}
    • Subject (sub): by default helena:env:{environment-uuid}:test:{test-uuid}
    • Audience (aud)
    • Discovery document and JWKS URI
  3. Configure the target to trust the issuer. Enter the issuer URL, the subject and the audience. The examples below cover Google Cloud, Entra ID and AWS.
  4. Select Run now. The run passes when the target issues a token.
  5. Add negative tests. Create more tests against the same target with variants such as Expired or Invalid signature. The target should switches to Refuse the token automatically.

Each test has its own issuer

Every test has a separate issuer URL and key set. A negative test therefore needs its issuer trusted on the target too. Otherwise it passes only because the issuer is unknown, not because the target checked the specific defect. For most variants, configure the target to trust each test's issuer URL and subject, just as you did for the valid test. The Untrusted issuer variant is the exception: it deliberately uses an issuer the target does not trust.

Field reference

Target

Field Key Required Default Supported values / notes
Exchange flow flow ✅ rfc8693 See Exchange flows.
Token endpoint / STS endpoint token_endpoint ✅ (OAuth flows) — Public HTTPS URL, up to 2048 characters. For AWS it is optional and defaults to https://sts.amazonaws.com/. You can use a regional endpoint such as https://sts.eu-west-1.amazonaws.com/.
audience parameter sts_audience — RFC 8693 only. The audience form parameter, i.e. the audience of the token you request (not the aud of the ID token). Google Cloud: //iam.googleapis.com/projects/…/providers/….
resource parameter resource — RFC 8693 only. A URI of the target service.
subject_token_type subject_token_type urn:ietf:params:oauth:token-type:jwt RFC 8693 only. urn:ietf:params:oauth:token-type:jwt or urn:ietf:params:oauth:token-type:id_token.
requested_token_type requested_token_type urn:ietf:params:oauth:token-type:access_token RFC 8693 only. Any token type URI.
scope scope — OAuth flows. Space-separated scopes, up to 1000 characters.
Client ID client_id ✅ (client assertion) — OAuth flows. Required for RFC 7523 client assertion (Entra: the application/client ID). Optional for the others.
Client secret secret client_secret — RFC 8693 and JWT bearer only. When set, it is sent with the client ID as HTTP Basic authentication (client_secret_basic). Encrypted, never shown again. On edit, tick Remove stored client secret to clear it.
Role ARN role_arn ✅ (AWS) — AWS only. arn:aws:iam::123456789012:role/NAME.
Role session name role_session_name helena- + run ID AWS only. 2–64 characters from A–Z a–z 0–9 _ + = , . @ -.
Duration (seconds) duration_seconds AWS default AWS only. 900–43200.
Extra form parameters extra_params — Up to 20 name/value pairs added to the request. They cannot override the token or secret parameters (assertion, client_assertion, subject_token, actor_token, WebIdentityToken, client_secret).
Extra request headers extra_headers — Up to 20 headers. You cannot set Authorization, Cookie, Host, Content-Type, Content-Length, Transfer-Encoding or Proxy-Authorization.

Workload ID token

Field Key Required Default Supported values / notes
Audience (aud) audiences ✅ — 1–10 values, one per line. A single value is sent as a string, several as an array. It must match what the target expects: e.g. the Google workload identity provider URL, api://AzureADTokenExchange or sts.amazonaws.com.
Subject (sub) subject helena:env:{env-uuid}:test:{test-uuid} Up to 255 characters. The default does not change when you rename the test.
Lifetime (seconds) lifetime_seconds ✅ 300 60–3600. Time between iat and exp.
Signing algorithm signing_alg ✅ RS256 RS256, RS384, RS512, ES256, ES384. Changing it generates a new signing key. See Signing keys.
Additional claims (JSON) additional_claims {} A JSON object with up to 25 custom claims, e.g. {"repository": "acme/app"}. You cannot set iss, sub, aud, exp, iat, nbf or jti.
Token variant token_variant ✅ valid See Token variants.

Every token also carries two claims Helena adds itself: helena_environment and helena_test, containing the environment and test UUIDs. Use them in your target's attribute conditions to trust only this test.

Expected result

Field Key Default Supported values / notes
The target should expected_outcome success Issue a token (success) or Refuse the token (rejection). It is preselected to Refuse when you choose a negative variant.
HTTP status expected_status — 100–599. Blank: any 2xx on success, any 4xx on refusal.
token_type expected_token_type — Success only. Case-insensitive exact match, e.g. Bearer.
issued_token_type expected_issued_token_type — RFC 8693, success only. Exact match, e.g. urn:ietf:params:oauth:token-type:access_token.
error code / Error Code expected_error — Refusal only. Exact match on the OAuth error (e.g. invalid_grant) or the AWS error code (e.g. InvalidIdentityToken).
Specification checks strict_spec_checks On See What gets checked.

Exchange flows

Flow (UI label) flow Request Helena sends Typical targets
RFC 8693 Token Exchange rfc8693 grant_type=urn:ietf:params:oauth:grant-type:token-exchange, subject_token=<ID token>, subject_token_type, plus optional audience, resource, scope, requested_token_type Google Cloud STS, Keycloak, most modern STSs
RFC 7523 client assertion rfc7523_client_assertion grant_type=client_credentials, client_id, client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer, client_assertion=<ID token>, scope Microsoft Entra federated identity credentials
RFC 7523 JWT bearer grant rfc7523_jwt_bearer grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer, assertion=<ID token>, scope Authorization servers that accept JWT bearer grants, including Helena's own service-account API
AWS STS AssumeRoleWithWebIdentity aws_sts Action=AssumeRoleWithWebIdentity, Version=2011-06-15, RoleArn, RoleSessionName, WebIdentityToken=<ID token>, optional DurationSeconds AWS IAM roles with an OIDC identity provider

Token variants

Variant (UI label) token_variant What Helena changes A correct target…
Valid token valid Nothing issues a token
Expired expired exp is 15 minutes in the past refuses
Not yet valid not_yet_valid nbf is 15 minutes in the future refuses
Wrong audience wrong_audience aud is https://wrong-audience.helena.invalid refuses
Untrusted issuer wrong_issuer iss is https://untrusted-issuer.helena.invalid, signed with the real key refuses
Invalid signature bad_signature Signed by an unpublished key under the real kid refuses
Unknown key ID (kid) unknown_kid Real key, random kid refuses
Unsigned (alg: none) alg_none No signature, alg: none refuses

The 15-minute offsets are well beyond common clock-skew allowances (5 minutes at most), so a correct target always refuses them.

What gets checked

When the target should issue a token:

  • The HTTP status is 2xx, or matches HTTP status.
  • A token was issued.
  • With Specification checks on:
    • token_type is present (RFC 6749 §5.1).
    • Cache-Control: no-store is present (OAuth flows).
    • issued_token_type is present (RFC 8693 §2.2.1).
    • For AWS, SubjectFromWebIdentityToken equals the token's sub.
  • The optional token_type and issued_token_type match.

When the target should refuse the token:

  • No token was issued.
  • The HTTP status is 4xx, or matches HTTP status.
  • With Specification checks on, an OAuth error (RFC 6749 §5.2) or an AWS Error/Code is present.
  • The optional error code matches.

Reading the results

Step Shows
Mint ID token Issuer, JWKS URI, variant, the JWT header and claims, and the token's fingerprint
Build token request Flow, URL, form parameters (the token is fingerprinted) and client authentication method
Send token request Status, selected response headers and the body. Issued credentials (access_token, id_token, refresh_token, AWS SecretAccessKey / SessionToken) are fingerprinted, and JWT access tokens are decoded for display.
Evaluate response Outcome, error, number of failed assertions

Signing keys

Each test signs with its own key pair. The kid is the key's RFC 7638 thumbprint, so it never changes for a given key and reveals nothing about it.

  • Rotate signing key (on the test page) generates a new key. The old key stays in the JWKS for 24 hours, so targets that cache JWKS keep validating while they refresh.
  • Changing the Signing algorithm also rotates the key.
  • Discovery and JWKS responses can be cached for up to 5 minutes, and are limited to 120 requests per minute per IP.
  • Disabling or deleting the test makes its issuer endpoints return 404, so the target can no longer fetch its keys. A target may still hold a cached copy for up to 5 minutes, or longer if it caches JWKS itself.

Examples

Google Cloud workload identity federation (RFC 8693)

  1. Create the test:

    Field Value
    Exchange flow RFC 8693 Token Exchange
    Token endpoint https://sts.googleapis.com/v1/token
    Audience (aud) https://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL/providers/PROVIDER
    audience parameter //iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL/providers/PROVIDER
    subject_token_type urn:ietf:params:oauth:token-type:jwt
    scope https://www.googleapis.com/auth/cloud-platform
  2. Create the provider in the pool:

    gcloud iam workload-identity-pools providers create-oidc helena \
      --location=global --workload-identity-pool=POOL \
      --issuer-uri="https://app.helena.dev/oidc/wif/TEST_UUID" \
      --attribute-mapping="google.subject=assertion.sub,attribute.helena_test=assertion.helena_test"
    
  3. Select Run now. Then add a test with the Expired variant to check that the pool refuses it.

Microsoft Entra ID federated identity credential (RFC 7523 client assertion)

Field Value
Exchange flow RFC 7523 client assertion
Token endpoint https://login.microsoftonline.com/TENANT_ID/oauth2/v2.0/token
Client ID The app registration's application (client) ID
Audience (aud) api://AzureADTokenExchange
scope https://graph.microsoft.com/.default

In Entra, open the app registration, then Certificates & secrets → Federated credentials → Add credential → Other issuer. Enter the test's Issuer URL and Subject (sub) from the test page, with audience api://AzureADTokenExchange.

AWS IAM (AssumeRoleWithWebIdentity)

  1. In IAM, create an OIDC identity provider with the test's Issuer URL as the provider URL and sts.amazonaws.com as the audience.
  2. Create a role whose trust policy allows sts:AssumeRoleWithWebIdentity for that provider, with these conditions:

    "Condition": {
      "StringEquals": {
        "app.helena.dev/oidc/wif/TEST_UUID:aud": "sts.amazonaws.com",
        "app.helena.dev/oidc/wif/TEST_UUID:sub": "helena:env:ENV_UUID:test:TEST_UUID"
      }
    }
    
  3. Create the test:

    Field Value
    Exchange flow AWS STS AssumeRoleWithWebIdentity
    Role ARN The role you just created
    Audience (aud) sts.amazonaws.com

Troubleshooting

Symptom Likely cause
A Valid token run is refused with invalid_grant or InvalidIdentityToken The target doesn't trust this test's issuer, or the subject or audience doesn't match. Compare the Mint ID token claims with the target's configuration.
Refused with an error about fetching keys or discovery The target cannot reach https://app.helena.dev. Check egress rules and allow-lists.
A negative variant passes but you expected it to fail That is the good result: the target refused the bad token. A run fails when the target accepts a token it should have refused.
Run status error The token endpoint URL is not public HTTPS, or the connection failed or timed out. See the Send token request step.
Tokens stop validating after Rotate signing key The target cached the JWKS for more than 24 hours. Shorten its JWKS cache.