Southclaws/oauthcli

A command line toolkit for checking, testing and verifying the capabilities of OAuth2.1 Authorisation Servers

1

stars

1

commits

Go

primary language

Sep 12, 2026

updated

oauth
oauth2
oauth2-client
oauth-cli
oauth-testing

README

oauthcli

A terminal toolkit for OAuth 2.0, OAuth 2.1 and OpenID Connect, built for people and for agents. It discovers what an authorization server publishes, validates it against the RFCs, obtains and decodes tokens with every grant, runs the browser and device flows, registers clients dynamically, and audits a whole installation with one command.

oauthcli discover https://accounts.google.com     # what does this issuer publish and support?
oauthcli check https://accounts.google.com        # is it conformant? one verdict per RFC
oauthcli token get -p dev --save                  # get a token with the configured client
oauthcli token inspect                            # decode and verify it
oauthcli token expect --sub 1234 --aud my-api     # assert facts about it; exit 5 on failure

Every command has --format json with a documented schema, so an agent uses the same tool a person does. The specification that generates the command tree, opencli.yaml, is also the behavioural contract the implementation is tested against.

Install

go install github.com/Southclaws/oauthcli@latest

The binary is called oauthcli.

For agents

oauthcli skill                              # usage guide with workflows and copy-paste recipes
oauthcli skill --full                       # plus a reference of every command and flag
oauthcli skill --out .claude/skills/oauth   # install it as a project skill

The bare command prints a cookbook, every subcommand explains itself in --help with examples, exit codes carry the verdict, and stdout carries only results so output pipes cleanly.

Exit codeMeaning
0Success
1Runtime error
2The server or a well-known document was unreachable
3The server answered with an OAuth error such as invalid_client
4A profile, saved token, metadata document or endpoint does not exist
5An expectation did not hold, or check found a non-conformance
6A token failed signature verification or is expired

Configuration

Settings come from flags, the environment, or a profile in a configuration file. Flags win.

oauthcli config init                                   # interactive: asks the issuer what it offers
oauthcli config init -n dev -i https://auth.example.com -c app --client-secret-file ./secret --non-interactive
oauthcli config list
oauthcli config path
PlatformPath
Windows%AppData%/oauth/config.yaml
macOS~/Library/Application Support/oauth/config.yaml
Linux$XDG_CONFIG_HOME/oauth/config.yaml, or ~/.config/oauth/config.yaml

--config or OAUTH_CONFIG points at any other file. A profile looks like this; examples/config.yaml has a worked set:

default: dev
profiles:
  dev:
    issuer: https://auth.example.com
    client:
      id: my-client
      secretFile: ~/.config/oauth/dev.secret # or secret:, or OAUTH_CLIENT_SECRET
      authMethod: auto # client_secret_basic, private_key_jwt, none, ...
    scopes: [openid, profile]
    redirectUri: http://127.0.0.1:8085/callback

Environment: OAUTH_ISSUER, OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, OAUTH_CLIENT_KEY, OAUTH_SCOPE, OAUTH_REDIRECT_URI, OAUTH_AUDIENCE, OAUTH_TOKEN, OAUTH_PROFILE, OAUTH_CONFIG, OAUTH_TIMEOUT, OAUTH_THEME, NO_COLOR.

Tokens saved with --save live in tokens.json next to the configuration file, mode 0600, and are used by later commands that take a TOKEN argument when none is given.

Commands

AreaCommands
Discoverydiscover, metadata, jwks, resource
Auditcheck
Tokenstoken get, token refresh, token inspect, token introspect, token revoke, token expect, token status, token clear, userinfo
Flowsflow code, flow device
Clientsclient register, client get, client update, client delete, cimd check, cimd new
Utilitiesreference, pkce, skill, config ...

Discovery

oauthcli discover https://auth.example.com          # every well-known document, capabilities, keys
oauthcli metadata https://auth.example.com          # RFC 8414 / OpenID discovery document, validated
oauthcli metadata --raw -f json | jq .token_endpoint
oauthcli jwks https://auth.example.com --kid abc --pem
oauthcli resource https://api.example.com --probe   # RFC 9728 metadata and the WWW-Authenticate challenge

discover tries both spellings of a path-bearing issuer: RFC 8414 inserts /.well-known/ after the host, OpenID Connect appends it.

Audit

oauthcli check https://auth.example.com             # anonymous
oauthcli check -p dev                               # with credentials: tokens, introspection, revocation, DPoP, PAR, device
oauthcli check -p dev --register --negative         # also DCR (creates and deletes a client) and a wrong-secret probe
oauthcli check -p dev --only rfc7636,oauth2.1 -f json
oauthcli check -p dev -f markdown > report.md

Each specification is reported as supported, conformant, supported, non-conformant, not supported, or not tested:

◆ Proof Key for Code Exchange (RFC 7636)   SUPPORTED · CONFORMANT
    ● code_challenge_methods_supported is advertised  RFC 8414 §2
    ● S256 is supported  §4.2
    ◐ plain is not offered  §7.2
        plain is advertised; a client may downgrade to it
    ● authorization request without PKCE is rejected  OAuth 2.1 §4.1.1

Specifications covered: RFC 8414, OpenID Connect Discovery, transport security (RFC 9700), RFC 7517, RFC 6749, RFC 7636, OAuth 2.1, RFC 9068, RFC 6750, OpenID Connect Core, RFC 7662, RFC 7009, RFC 8628, RFC 9126, RFC 9449, RFC 8707, RFC 7523, RFC 8693, RFC 7591 and 7592, RFC 9728, RFC 9207, RFC 8705, RFC 9101, RFC 9396, and client ID metadata documents.

Anonymous checks cover metadata, key sets, TLS, error handling at the token and authorization endpoints, and whether the authorization endpoint rejects a request without PKCE. Credentials unlock the token endpoint: a client credentials token is obtained, decoded, verified against the key set, introspected and revoked; DPoP binding, resource indicators, PAR and the device flow are tried where advertised. Checks that create state are opt-in. A --token you supply is validated but never revoked.

Tokens

oauthcli token get -p dev --save                              # client_credentials
oauthcli token get -i URL -c ID --client-secret-file F -s read
oauthcli token get --dpop --save                              # DPoP-bound (RFC 9449)
oauthcli token get -g token-exchange --subject-token "$AT"    # RFC 8693
oauthcli token get -g jwt-bearer --assertion "$JWT"           # RFC 7523
oauthcli token refresh --save
oauthcli token inspect - < token.txt                          # decode, verify signature, check timing
oauthcli token introspect                                     # RFC 7662
oauthcli token revoke --verify                                # RFC 7009, then confirm inactive
oauthcli userinfo

Client authentication is picked from what the server advertises and the credentials given: private_key_jwt with --client-key, client_secret_basic with a secret, none for a public client. --auth-method forces one.

Expectations

oauthcli token expect --sub 1234 --aud https://api.example.com --scope-includes read
oauthcli token expect --claim 'email~@example\.com$' --claim role=admin --claim 'groups+=staff'
oauthcli token expect --typ at+jwt --alg RS256 --max-ttl 1h --has jti --active

Exit 0 when every expectation holds, 5 when one does not. name=value compares the string form, name~regex matches, name+=member requires membership in an array claim.

Flows

oauthcli flow code -p dev                    # authorization code + PKCE; opens the browser, saves the token
oauthcli flow code -s openid,email --par     # push the request first (RFC 9126)
oauthcli flow code --no-browser              # print the URL for a person to open
oauthcli flow code --manual -r https://app/callback   # no listener; paste the redirect URL back
oauthcli flow device --qr                    # device flow (RFC 8628) with a QR code

flow code listens on a loopback redirect URI, generates state, PKCE and (with openid) nonce, validates the callback including the RFC 9207 iss parameter, exchanges the code, and validates the ID token: issuer, audience, nonce, at_hash.

Clients

oauthcli client register -n "my client" -r http://127.0.0.1:8085/callback --save   # RFC 7591
oauthcli client get | update | delete                                               # RFC 7592
oauthcli cimd new https://app.example.com/oauth/client.json -r https://app.example.com/callback
oauthcli cimd check https://app.example.com/oauth/client.json

Piping

oauthcli token get -f plain | oauthcli token inspect -
oauthcli discover -f json | jq '.capabilities[] | select(.supported) | .id'
oauthcli check -f json | jq '.specs[] | {id, verdict}'
oauthcli jwks -f plain | cut -f1

--trace logs every HTTP exchange on stderr with secrets redacted.

Completions

Shell completion comes from carapace:

oauthcli _carapace zsh >> ~/.zshrc        # or bash, fish, nushell, elvish, xonsh, powershell

Profiles, issuers, client ids, specification ids and scopes (fetched from the active issuer's scopes_supported) are completed from real values.

Specifications

The rfcs/ folder holds the verbatim text of every specification the checks cite: RFC 6749, 6750, 7009, 7517, 7518, 7519, 7523, 7591, 7592, 7636, 7638, 7662, 8252, 8414, 8628, 8693, 8707, 8725, 8996, 9068, 9126, 9207, 9325, 9449, 9700, 9728, the OAuth 2.1 and client ID metadata document drafts, and OpenID Connect Core and Discovery. Each check names the section it enforces, and the texts are embedded in the binary, so a finding can be read next to the sentence that defines it:

oauthcli reference                              # list the embedded specifications
oauthcli reference rfc6749 --section 5.2         # one section, page headers removed
oauthcli reference oidc-core --section 3.1.3.7 | less

The IETF texts are reproduced unmodified under the IETF Trust Legal Provisions; the OpenID Foundation texts under the licence in their Notices appendix, for the purpose of implementing the specifications. See rfcs/README.md.

Development

The command tree is generated from opencli.yaml by OpenCLI:

opencli validate opencli.yaml
go generate ./internal/cli      # regenerates internal/cligen
go test ./...

Handlers in internal/cli implement behaviour only; flags, arguments, help text and output schemas come from the specification. Tests check the implementation against it: every leaf command is covered by the agent guide, every description renders correctly, and an in-process authorization server exercises the whole audit in both a conformant and a deliberately broken configuration.

Contributors

Southclaws

1 commits

Southclaws/oauthcli

A command line toolkit for checking, testing and verifying the capabilities of OAuth2.1 Authorisation Servers

1

stars

1

commits

Go

primary language

Sep 12, 2026

updated

oauth
oauth2
oauth2-client
oauth-cli
oauth-testing

README

oauthcli

A terminal toolkit for OAuth 2.0, OAuth 2.1 and OpenID Connect, built for people and for agents. It discovers what an authorization server publishes, validates it against the RFCs, obtains and decodes tokens with every grant, runs the browser and device flows, registers clients dynamically, and audits a whole installation with one command.

oauthcli discover https://accounts.google.com     # what does this issuer publish and support?
oauthcli check https://accounts.google.com        # is it conformant? one verdict per RFC
oauthcli token get -p dev --save                  # get a token with the configured client
oauthcli token inspect                            # decode and verify it
oauthcli token expect --sub 1234 --aud my-api     # assert facts about it; exit 5 on failure

Every command has --format json with a documented schema, so an agent uses the same tool a person does. The specification that generates the command tree, opencli.yaml, is also the behavioural contract the implementation is tested against.

Install

go install github.com/Southclaws/oauthcli@latest

The binary is called oauthcli.

For agents

oauthcli skill                              # usage guide with workflows and copy-paste recipes
oauthcli skill --full                       # plus a reference of every command and flag
oauthcli skill --out .claude/skills/oauth   # install it as a project skill

The bare command prints a cookbook, every subcommand explains itself in --help with examples, exit codes carry the verdict, and stdout carries only results so output pipes cleanly.

Exit codeMeaning
0Success
1Runtime error
2The server or a well-known document was unreachable
3The server answered with an OAuth error such as invalid_client
4A profile, saved token, metadata document or endpoint does not exist
5An expectation did not hold, or check found a non-conformance
6A token failed signature verification or is expired

Configuration

Settings come from flags, the environment, or a profile in a configuration file. Flags win.

oauthcli config init                                   # interactive: asks the issuer what it offers
oauthcli config init -n dev -i https://auth.example.com -c app --client-secret-file ./secret --non-interactive
oauthcli config list
oauthcli config path
PlatformPath
Windows%AppData%/oauth/config.yaml
macOS~/Library/Application Support/oauth/config.yaml
Linux$XDG_CONFIG_HOME/oauth/config.yaml, or ~/.config/oauth/config.yaml

--config or OAUTH_CONFIG points at any other file. A profile looks like this; examples/config.yaml has a worked set:

default: dev
profiles:
  dev:
    issuer: https://auth.example.com
    client:
      id: my-client
      secretFile: ~/.config/oauth/dev.secret # or secret:, or OAUTH_CLIENT_SECRET
      authMethod: auto # client_secret_basic, private_key_jwt, none, ...
    scopes: [openid, profile]
    redirectUri: http://127.0.0.1:8085/callback

Environment: OAUTH_ISSUER, OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, OAUTH_CLIENT_KEY, OAUTH_SCOPE, OAUTH_REDIRECT_URI, OAUTH_AUDIENCE, OAUTH_TOKEN, OAUTH_PROFILE, OAUTH_CONFIG, OAUTH_TIMEOUT, OAUTH_THEME, NO_COLOR.

Tokens saved with --save live in tokens.json next to the configuration file, mode 0600, and are used by later commands that take a TOKEN argument when none is given.

Commands

AreaCommands
Discoverydiscover, metadata, jwks, resource
Auditcheck
Tokenstoken get, token refresh, token inspect, token introspect, token revoke, token expect, token status, token clear, userinfo
Flowsflow code, flow device
Clientsclient register, client get, client update, client delete, cimd check, cimd new
Utilitiesreference, pkce, skill, config ...

Discovery

oauthcli discover https://auth.example.com          # every well-known document, capabilities, keys
oauthcli metadata https://auth.example.com          # RFC 8414 / OpenID discovery document, validated
oauthcli metadata --raw -f json | jq .token_endpoint
oauthcli jwks https://auth.example.com --kid abc --pem
oauthcli resource https://api.example.com --probe   # RFC 9728 metadata and the WWW-Authenticate challenge

discover tries both spellings of a path-bearing issuer: RFC 8414 inserts /.well-known/ after the host, OpenID Connect appends it.

Audit

oauthcli check https://auth.example.com             # anonymous
oauthcli check -p dev                               # with credentials: tokens, introspection, revocation, DPoP, PAR, device
oauthcli check -p dev --register --negative         # also DCR (creates and deletes a client) and a wrong-secret probe
oauthcli check -p dev --only rfc7636,oauth2.1 -f json
oauthcli check -p dev -f markdown > report.md

Each specification is reported as supported, conformant, supported, non-conformant, not supported, or not tested:

◆ Proof Key for Code Exchange (RFC 7636)   SUPPORTED · CONFORMANT
    ● code_challenge_methods_supported is advertised  RFC 8414 §2
    ● S256 is supported  §4.2
    ◐ plain is not offered  §7.2
        plain is advertised; a client may downgrade to it
    ● authorization request without PKCE is rejected  OAuth 2.1 §4.1.1

Specifications covered: RFC 8414, OpenID Connect Discovery, transport security (RFC 9700), RFC 7517, RFC 6749, RFC 7636, OAuth 2.1, RFC 9068, RFC 6750, OpenID Connect Core, RFC 7662, RFC 7009, RFC 8628, RFC 9126, RFC 9449, RFC 8707, RFC 7523, RFC 8693, RFC 7591 and 7592, RFC 9728, RFC 9207, RFC 8705, RFC 9101, RFC 9396, and client ID metadata documents.

Anonymous checks cover metadata, key sets, TLS, error handling at the token and authorization endpoints, and whether the authorization endpoint rejects a request without PKCE. Credentials unlock the token endpoint: a client credentials token is obtained, decoded, verified against the key set, introspected and revoked; DPoP binding, resource indicators, PAR and the device flow are tried where advertised. Checks that create state are opt-in. A --token you supply is validated but never revoked.

Tokens

oauthcli token get -p dev --save                              # client_credentials
oauthcli token get -i URL -c ID --client-secret-file F -s read
oauthcli token get --dpop --save                              # DPoP-bound (RFC 9449)
oauthcli token get -g token-exchange --subject-token "$AT"    # RFC 8693
oauthcli token get -g jwt-bearer --assertion "$JWT"           # RFC 7523
oauthcli token refresh --save
oauthcli token inspect - < token.txt                          # decode, verify signature, check timing
oauthcli token introspect                                     # RFC 7662
oauthcli token revoke --verify                                # RFC 7009, then confirm inactive
oauthcli userinfo

Client authentication is picked from what the server advertises and the credentials given: private_key_jwt with --client-key, client_secret_basic with a secret, none for a public client. --auth-method forces one.

Expectations

oauthcli token expect --sub 1234 --aud https://api.example.com --scope-includes read
oauthcli token expect --claim 'email~@example\.com$' --claim role=admin --claim 'groups+=staff'
oauthcli token expect --typ at+jwt --alg RS256 --max-ttl 1h --has jti --active

Exit 0 when every expectation holds, 5 when one does not. name=value compares the string form, name~regex matches, name+=member requires membership in an array claim.

Flows

oauthcli flow code -p dev                    # authorization code + PKCE; opens the browser, saves the token
oauthcli flow code -s openid,email --par     # push the request first (RFC 9126)
oauthcli flow code --no-browser              # print the URL for a person to open
oauthcli flow code --manual -r https://app/callback   # no listener; paste the redirect URL back
oauthcli flow device --qr                    # device flow (RFC 8628) with a QR code

flow code listens on a loopback redirect URI, generates state, PKCE and (with openid) nonce, validates the callback including the RFC 9207 iss parameter, exchanges the code, and validates the ID token: issuer, audience, nonce, at_hash.

Clients

oauthcli client register -n "my client" -r http://127.0.0.1:8085/callback --save   # RFC 7591
oauthcli client get | update | delete                                               # RFC 7592
oauthcli cimd new https://app.example.com/oauth/client.json -r https://app.example.com/callback
oauthcli cimd check https://app.example.com/oauth/client.json

Piping

oauthcli token get -f plain | oauthcli token inspect -
oauthcli discover -f json | jq '.capabilities[] | select(.supported) | .id'
oauthcli check -f json | jq '.specs[] | {id, verdict}'
oauthcli jwks -f plain | cut -f1

--trace logs every HTTP exchange on stderr with secrets redacted.

Completions

Shell completion comes from carapace:

oauthcli _carapace zsh >> ~/.zshrc        # or bash, fish, nushell, elvish, xonsh, powershell

Profiles, issuers, client ids, specification ids and scopes (fetched from the active issuer's scopes_supported) are completed from real values.

Specifications

The rfcs/ folder holds the verbatim text of every specification the checks cite: RFC 6749, 6750, 7009, 7517, 7518, 7519, 7523, 7591, 7592, 7636, 7638, 7662, 8252, 8414, 8628, 8693, 8707, 8725, 8996, 9068, 9126, 9207, 9325, 9449, 9700, 9728, the OAuth 2.1 and client ID metadata document drafts, and OpenID Connect Core and Discovery. Each check names the section it enforces, and the texts are embedded in the binary, so a finding can be read next to the sentence that defines it:

oauthcli reference                              # list the embedded specifications
oauthcli reference rfc6749 --section 5.2         # one section, page headers removed
oauthcli reference oidc-core --section 3.1.3.7 | less

The IETF texts are reproduced unmodified under the IETF Trust Legal Provisions; the OpenID Foundation texts under the licence in their Notices appendix, for the purpose of implementing the specifications. See rfcs/README.md.

Development

The command tree is generated from opencli.yaml by OpenCLI:

opencli validate opencli.yaml
go generate ./internal/cli      # regenerates internal/cligen
go test ./...

Handlers in internal/cli implement behaviour only; flags, arguments, help text and output schemas come from the specification. Tests check the implementation against it: every leaf command is covered by the agent guide, every description renders correctly, and an in-process authorization server exercises the whole audit in both a conformant and a deliberately broken configuration.

Contributors

Southclaws

1 commits

Languages

Go

100.0%