Oidc Diagnose
@tagur/oidc-diagnosev2026.10.16.1
01README
IdP-agnostic OpenID Connect diagnostics for swamp — troubleshooting of broken login flows against any standards-compliant provider (Okta, Auth0, Entra ID, Keycloak, Zitadel, Cognito, Google, …).
The read-only methods (discover, jwks, decode-token) need no credentials and no vault. exchange-code and resolve-groups need a client secret, via a vault.
Methods:
- discover: fetch /.well-known/openid-configuration and validate it (issuer match, required endpoints, PKCE methods, scopes, grants).
- jwks: fetch the provider JWKS and report each key's kid, kty, alg, use.
- decode-token: decode a JWT (header + claims; signature is NOT cryptographically verified) and sanity-check alg, kid, iss, aud, exp, nbf, iat against the clock, the issuer, and the discovered JWKS. Optionally assert required claims (groups, email, roles, …). Entra groups-overage is informational by default (set requireInlineGroups to make it an error).
- exchange-code: exchange an OAuth2 authorization code at the token endpoint (confidential client; secret from a vault), then decode and claim-check the returned id_token. Reproduces the server-side exchange a proxy (oauth2-proxy, Skipper, Pomerium) performs so you can inspect the real user claims the browser never receives.
- resolve-groups: expand an Entra groups-overage reference into the actual group list — mints a Microsoft Graph token via a client-credentials grant (secret from a vault) and calls the _claim_sources endpoint from a prior token. The app registration needs Graph application permissions with admin consent.
Every method writes a diagnostic resource even on failure, so a broken
endpoint leaves a truthful, queryable record. Endpoints discovered by
discover feed jwks and exchange-code via stored data / CEL.
Quick Start
swamp extension pull @tagur/oidc-diagnose
swamp behavior create @tagur/oidc-diagnose login-check \
--global-arg issuer=https://login.example.com
swamp behavior run login-check discover
swamp behavior run login-check jwks
swamp behavior run login-check decode-token \
--arg token=<jwt> --arg expectedAudience=<client-id> \
--arg 'requiredClaims=["groups","email"]'
swamp data query 'behavior("login-check")' \
--select '{"name": name, "findings": attributes.findings}'02Behaviors
@tagur/oidc-diagnosev2026.10.16.1oidc-diagnose.ts
Global Arguments
| Argument | Type | Description |
|---|---|---|
| issuer | string | OIDC issuer / provider base URL (e.g. https://login.example.com or |
| timeoutMs? | number | Per-request HTTP timeout in milliseconds |
| caCert? | string | Optional PEM CA certificate for internal / self-signed TLS endpoints |
| clockSkewSeconds? | number | Allowed clock skew (seconds) when checking exp / nbf / iat |
fn discover()
Fetch <issuer>/.well-known/openid-configuration and validate it:
fn jwks(jwksUri?: string)
Fetch the provider JWKS and report each key's kid, kty, alg, and use.
| Argument | Type | Description |
|---|---|---|
| jwksUri? | string | Explicit JWKS URI. Overrides the URI resolved from discovery. |
fn decode-token(token: string, expectedAudience?: string, requiredClaims?: array, expectedGroups?: array, requireInlineGroups?: boolean)
Decode a JWT (header + claims; signature is NOT cryptographically
| Argument | Type | Description |
|---|---|---|
| token | string | The JWT to decode (an access_token or id_token). Treated as |
| expectedAudience? | string | Expected 'aud' value (the client_id or resource identifier). When |
| requiredClaims? | array | Claim names that must be present and non-empty (e.g. |
| expectedGroups? | array | Specific group values (ids or names) the 'groups' claim must |
| requireInlineGroups? | boolean | When true, require the 'groups' claim inline in the token: Entra |
fn exchange-code(code: string, clientId: string, clientSecret: string, redirectUri: string, codeVerifier?: string, requiredClaims?: array, expectedGroups?: array, requireInlineGroups?: boolean)
Exchange an OAuth2 authorization code at the token endpoint
| Argument | Type | Description |
|---|---|---|
| code | string | The authorization code from the redirect callback (?code=...). |
| clientId | string | The OAuth2 client_id (application id). Also used as the expected |
| clientSecret | string | The client secret. Supply via a vault reference |
| redirectUri | string | The redirect_uri registered for the app; must match the one used |
| codeVerifier? | string | PKCE code_verifier, if the authorization request used PKCE. |
| requiredClaims? | array | Claim names the id_token must contain (e.g. ['groups','email']). |
| expectedGroups? | array | Group values the 'groups' claim must include. |
| requireInlineGroups? | boolean | When true, Entra groups-overage is an error instead of info. |
fn resolve-groups(clientId: string, clientSecret: string, graphScope?: string)
Expand an Entra groups-overage reference into the actual group list.
| Argument | Type | Description |
|---|---|---|
| clientId | string | The application (client) id used for the client-credentials grant. |
| clientSecret | string | The client secret; supply via a vault reference |
| graphScope? | string | OAuth2 scope for the Graph token. Defaults to the Microsoft Graph |
Resources
discovery(infinite)— Validated OIDC discovery document: resolved endpoints and findings
jwks(infinite)— Fetched JWKS summary: key ids, algorithms, and findings
token(infinite)— Decoded JWT: header, claims, and standard-claim validation findings
groups(infinite)— Group membership resolved from an Entra groups-overage
03Previous Versions
2026.10.15.1MIT
04Stats
A
100 / 100
Downloads
19
Archive size
25.3 KB
- Has README or module doc2/2earned
- README has a code example1/1earned
- README is substantive1/1earned
- Most symbols documented1/1earned
- No slow types (deprecated)1/1earned
- Dependencies pass trust audit2/2earned
- Has description1/1earned
- Platform support declared (or universal)2/2earned
- License2/2earned
- Verified public repository2/2earned
05Platforms
06Labels