Winix.MkAuth 0.4.0

Prefix Reserved
dotnet tool install --global Winix.MkAuth --version 0.4.0
                    
This package contains a .NET tool you can call from the shell/command line.
dotnet new tool-manifest
                    
if you are setting up this repo
dotnet tool install --local Winix.MkAuth --version 0.4.0
                    
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=Winix.MkAuth&version=0.4.0
                    
nuke :add-package Winix.MkAuth --version 0.4.0
                    

mkauth

Compute HTTP Authorization headers (OAuth 1.0a, JWT, Basic, Bearer, Azure Storage) for curl and scripts. Single native binary, no runtime, cross-platform.

mkauth is a pure, stateless calculator — it prints one header line to stdout and exits. Use it in command substitution with curl -H "$(mkauth …)":

curl -H "$(mkauth oauth1 --method GET --url https://api.example.com/v1/thing \
              --consumer-key abc --consumer-secret 'vault:api/consumer' \
              --token def --token-secret 'vault:api/token')" \
     https://api.example.com/v1/thing

Install

Scoop (Windows)

scoop bucket add winix https://github.com/Yortw/winix
scoop install winix/mkauth

Winget (Windows, stable releases)

winget install Winix.MkAuth

.NET Tool (cross-platform)

dotnet tool install -g Winix.MkAuth

Direct Download

Download native binaries from GitHub Releases.

Usage

mkauth basic     --user NAME --password REF [options]
mkauth bearer    --token REF [options]
mkauth oauth1    --method VERB --url URL --consumer-key K --consumer-secret REF [options]
mkauth jwt       --alg ALG --key REF [claim flags] [options]
mkauth azure-storage --account NAME --key REF --method VERB --url URL [options]

All flags are listed in mkauth --help; per-subcommand flags are documented in the tables below.

The computed header is written to stdout as a Name: Value line, suitable for curl -H "$(…)". --show-base-string debug output goes to stderr. Warnings (literal-secret exposure, PLAINTEXT over non-HTTPS) go to stderr.

secret references

Every secret-bearing flag takes a secret reference — a string with a scheme prefix. Secrets are never required as bare values on argv (which would expose them in shell history and ps output).

Reference Source Notes
env:NAME Environment variable NAME Value read at runtime, not from argv. Surrounding whitespace trimmed.
file:PATH File contents Surrounding whitespace trimmed (see below).
vault:NS/KEY OS keychain via Winix.SecretStore DPAPI (Windows) / Keychain (macOS) / libsecret (Linux). Splits on the first /; NS cannot contain /, KEY may. Surrounding whitespace trimmed.
- or stdin Standard input Safe; at most one secret per invocation may use stdin. Surrounding whitespace trimmed.
literal:VALUE The literal value Emits a ps/history-exposure warning to stderr. Used verbatim (no trimming); an empty or whitespace-only literal: is allowed. Explicit escape hatch only.

Whitespace trimming: every reference-resolved source (env:, file:, vault:, stdin) has all surrounding whitespace stripped (leading and trailing, including trailing newlines), so the same key signs the same way regardless of how it is delivered. Only literal: is used verbatim.

Empty secrets: a secret resolved from env:/file:/vault:/stdin that is empty (or whitespace-only, which trims to empty) is an error (exit 126) naming the source — signing with an empty key produces a silently-wrong header. To send an empty secret deliberately (e.g. an RFC 7617 empty password), use an explicit empty literal:.

F6 note: vault:NS/KEY splits on the first /. A namespace cannot contain /; a key may contain / characters after the first slash.

basic — Basic authentication

Computes Authorization: Basic base64(user:password) per RFC 7617.

# Hardcoded user, password from environment variable
mkauth basic --user alice --password env:API_PASSWORD

# Both from the OS keychain
mkauth basic --user alice --password 'vault:api/password'

# Feed to curl
curl -H "$(mkauth basic --user alice --password env:API_PASSWORD)" https://api.example.com/data
basic options
Flag Required Description
--user NAME yes Username. A username containing : is a usage error.
--password REF yes Secret reference for the password.

bearer — Bearer token

Outputs Authorization: Bearer <token> per RFC 6750. Pure passthrough — the resolved token value is used as-is.

# Token from environment variable
mkauth bearer --token env:ACCESS_TOKEN

# Token from keychain
mkauth bearer --token 'vault:myapp/token'

# Feed to curl
curl -H "$(mkauth bearer --token env:ACCESS_TOKEN)" https://api.example.com/data
bearer options
Flag Required Description
--token REF yes Secret reference for the bearer token.

oauth1 — OAuth 1.0a signed header (flagship)

Computes the full OAuth 1.0a Authorization header (RFC 5849) including the percent-encoded signature base string, signing key, and HMAC or PLAINTEXT signature.

Scope note: mkauth oauth1 signs a request given credentials. It does not perform the OAuth 1.0a 3-legged request-token→authorize→access-token dance. Token acquisition is a separate flow, out of scope for this tool.

# 2-legged (no token) with HMAC-SHA1
mkauth oauth1 --method POST --url https://api.example.com/data \
              --consumer-key mykey --consumer-secret 'vault:myapp/consumer-secret'

# 3-legged with HMAC-SHA256
mkauth oauth1 --method GET --url 'https://api.example.com/timeline?count=10' \
              --consumer-key mykey --consumer-secret env:CONSUMER_SECRET \
              --token mytoken --token-secret env:TOKEN_SECRET \
              --signature-method HMAC-SHA256

# Include body params in signature (application/x-www-form-urlencoded bodies)
mkauth oauth1 --method POST --url https://api.example.com/post \
              --consumer-key k --consumer-secret env:CS \
              --param "status=Hello World" --param "source=myapp"

# Show the computed signature base string (debug)
mkauth oauth1 --method GET --url https://api.example.com/thing \
              --consumer-key k --consumer-secret env:CS \
              --show-base-string

# Feed to curl
curl -H "$(mkauth oauth1 --method GET --url https://api.example.com/v1/me \
              --consumer-key abc --consumer-secret 'vault:api/consumer' \
              --token def --token-secret 'vault:api/token')" \
     https://api.example.com/v1/me
oauth1 options
Flag Required Default Description
--method VERB yes — HTTP method (upper-cased for the base string).
--url URL yes — Full request URL. Query params are parsed out and included in the signature base.
--consumer-key K yes — oauth_consumer_key.
--consumer-secret REF yes — Secret reference for the consumer secret.
--token T no — oauth_token (omit for 2-legged).
--token-secret REF no empty Secret reference for the token secret. Empty = 2-legged.
--signature-method M no HMAC-SHA1 Signature method: HMAC-SHA1, HMAC-SHA256, PLAINTEXT.
--param k=v no — Extra/body params for the signature base (repeatable).
--realm R no — realm value (not part of the signature base).
--timestamp N no auto oauth_timestamp (Unix seconds). Override for reproducibility.
--nonce S no auto oauth_nonce. Override for reproducibility.
Signature algorithm

The signature base string is METHOD & pct(base_url) & pct(normalized_params), where:

  • base_url = scheme + authority + path (scheme/host lowercased, default port dropped, no query/fragment)
  • normalized_params = RFC 3986 percent-encoded, lexicographically-sorted, &-joined merge of URL query params + --param params + oauth_* params (excluding realm and oauth_signature)
  • Signing key = pct(consumer_secret) & pct(token_secret)

PLAINTEXT warning: PLAINTEXT sends the signing key as the signature. mkauth warns to stderr if the URL scheme is not https.

jwt — JWT minting and signing

Mints a JSON Web Token (RFC 7519) and wraps it as Authorization: Bearer <jwt>. Useful for service-account auth (Google service accounts, GitHub Apps) that require a freshly signed JWT per call.

# HS256 with a shared secret
mkauth jwt --alg HS256 --key 'vault:myapp/jwt-secret' \
           --iss myapp --sub user123 --exp 1h

# RS256 with a PEM private key file
mkauth jwt --alg RS256 --key file:/path/to/private.pem \
           --iss myapp@project.iam.gserviceaccount.com --sub api@example.com \
           --aud https://api.example.com/ --exp 1h --iat

# ES256 with key from keychain
mkauth jwt --alg ES256 --key 'vault:apps/jwt-key' \
           --claim scope=read:data --claim-num priority=1 \
           --kid my-key-id

# Arbitrary claims from a JSON file
mkauth jwt --alg HS256 --key env:JWT_SECRET \
           --claims-file /path/to/claims.json --exp 15m

# Header value only: strips the 'Authorization:' name but KEEPS the 'Bearer ' prefix -> 'Bearer <jwt>'
mkauth jwt --alg HS256 --key env:JWT_SECRET --sub user --exp 1h --value-only

# Feed to curl
curl -H "$(mkauth jwt --alg RS256 --key file:private.pem \
              --iss svc@project.iam.gserviceaccount.com \
              --aud https://api.example.com/ --exp 1h --iat)" \
     https://api.example.com/data
jwt options
Flag Required Default Description
--alg ALG no HS256 Signing algorithm: HS256/384/512, RS256/384/512, ES256/384/512. ES algs require a matching curve (see below).
--key REF yes — HS: shared secret. RS/ES: PEM private key (use file: or vault:).
--claim k=v no — String claim (repeatable).
--claim-num k=v no — Numeric claim — v is parsed as a number (for NumericDate fields).
--claim-json k=v no — Raw JSON claim — v is parsed as raw JSON.
--claims-file PATH no — JSON object file merged into the claim set.
--claims-stdin no off Merge a JSON object from stdin. Mutually exclusive with --key stdin.
--iss S no — iss (issuer) standard claim.
--sub S no — sub (subject) standard claim.
--aud S no — aud (audience) standard claim.
--exp DURATION no — exp = now + DURATION (e.g. 1h, 30m, 3600s).
--iat no off Set iat to now.
--nbf DURATION no — nbf = now + DURATION.
--kid S no — kid JOSE header parameter.
--header k=v no — Additional JOSE header parameters (repeatable; accepts name=value or name: value). alg and typ are reserved (set by --alg) and rejected as a usage error; distinct-cased names like Alg are allowed.

ES curve requirement: an ES* algorithm fixes the EC curve (RFC 7518 §3.4). The supplied PEM key must use the matching curve, or signing fails with a runtime error (exit 126):

Algorithm Required curve
ES256 P-256
ES384 P-384
ES512 P-521

azure-storage — Azure Storage SharedKey

Computes the Authorization: SharedKey account:signature header for Azure Storage REST API requests (Blob, Queue, and File services).

# Sign a Blob GET request
mkauth azure-storage --account mystorageacct \
                     --key env:STORAGE_ACCOUNT_KEY \
                     --method GET \
                     --url 'https://mystorageacct.blob.core.windows.net/mycontainer/myblob' \
                     --x-ms-version 2023-11-03

# Show the StringToSign (debug)
mkauth azure-storage --account mystorageacct --key env:STORAGE_KEY \
                     --method PUT --url 'https://mystorageacct.blob.core.windows.net/c/b' \
                     --header 'Content-Type: application/octet-stream' \
                     --show-base-string

# Feed to curl (note: x-ms-date and x-ms-version must match the headers you send)
DATE=$(date -u '+%a, %d %b %Y %H:%M:%S GMT')
curl -H "x-ms-date: $DATE" \
     -H "x-ms-version: 2023-11-03" \
     -H "$(mkauth azure-storage --account mystorageacct --key env:STORAGE_KEY \
               --method GET --url 'https://mystorageacct.blob.core.windows.net/c/b' \
               --x-ms-date "$DATE" --x-ms-version 2023-11-03)" \
     'https://mystorageacct.blob.core.windows.net/c/b'
azure-storage options
Flag Required Default Description
--account NAME yes — Storage account name.
--key REF yes — Account key (base64-encoded); decoded to the HMAC key.
--method VERB yes — HTTP method.
--url URL yes — Request URL → canonicalized resource (/account/path + sorted query).
--x-ms-date S no auto (RFC1123 GMT) The x-ms-date header value. Must match the value sent with the request.
--x-ms-version V no pinned default x-ms-version header value. Signing assumes version 2015-02-21 or later, where Content-Length is signed blank (not 0) when zero. Earlier versions are not supported.
--header NAME:VAL no — Additional headers (repeatable). Accepts HTTP-style name: value or name=value. Used for fixed StringToSign headers (Content-Type, Content-Length, …) and x-ms-* CanonicalizedHeaders. (x-ms-date/x-ms-version have dedicated flags — prefer those.)

Common flags (all subcommands)

Flag Description
--value-only Print the header value only — strips the Authorization: name but keeps the scheme prefix (e.g. Bearer <jwt>, Basic <b64>). Useful for scripts or config that store a header value rather than a full header line. (Note: this is not a bare token — curl --oauth2-bearer wants the raw token, so don't feed it --value-only output.)
--json Emit a JSON envelope to stdout: {"scheme":"…","header_name":"Authorization","header_value":"…"}. With --show-base-string, adds "base_string":"…".
--show-base-string Emit the computed signature base string or StringToSign to stderr (or in --json). Applies to oauth1 and azure-storage.
--describe Emit structured JSON metadata for AI discoverability.
--help, -h Show help and exit.
--version Show version and exit.
--color[=auto\|always\|never] Coloured output: auto (default when omitted), always, or never.
--no-color Disable coloured output. Respects NO_COLOR.

Composability with curl

The primary use pattern — command substitution feeding curl -H:

# OAuth 1.0a
curl -H "$(mkauth oauth1 --method POST --url https://api.example.com/data \
              --consumer-key k --consumer-secret env:CS \
              --token t --token-secret env:TS)" \
     -d '{"key":"value"}' https://api.example.com/data

# JWT service account
curl -H "$(mkauth jwt --alg RS256 --key file:service-account.pem \
              --iss svc@project.iam.gserviceaccount.com \
              --aud https://api.example.com/ --exp 1h --iat)" \
     https://api.example.com/data

# Basic auth (keep password out of shell history)
curl -H "$(mkauth basic --user alice --password 'vault:myapp/password')" \
     https://api.example.com/protected

# Bearer token from keychain
curl -H "$(mkauth bearer --token 'vault:myapp/access-token')" \
     https://api.example.com/api

For other HMAC schemes, use digest

mkauth handles OAuth 1.0a, JWT, Basic, Bearer, and Azure SharedKey. For other body-based HMAC schemes (Stripe webhooks, GitHub webhooks, generic timestamp.body patterns), use digest --hmac directly:

# Generic HMAC-SHA256 of a body (Stripe / GitHub style)
printf '%s.%s' "$ts" "$body" | digest --hmac sha256 --key-env WEBHOOK_SECRET --base64

# Stripe webhook signature
echo -n "${ts}.${payload}" | digest --hmac sha256 --key-env STRIPE_WEBHOOK_SECRET --base64

# HMAC over a file
digest --hmac sha256 --key-file signing.key payload.json

AWS SigV4 is covered by curl --aws-sigv4 (built in to curl). mkauth does not duplicate it.

Storing secrets in the keychain

Use envvault to store secrets in the OS keychain and pass them to mkauth via vault: references:

# Store once
envvault set myapp consumer-secret "my-consumer-secret-value"

# Use in mkauth (secret never touches argv or shell history)
mkauth oauth1 --method GET --url https://api.example.com/me \
              --consumer-key mykey --consumer-secret 'vault:myapp/consumer-secret'

Exit Codes

Code Meaning
0 Success. The computed header was written to stdout.
125 Usage error — unknown subcommand, missing required flag, invalid flag value, conflicting flags (e.g. two stdin references), or unexpected positional. Stderr carries the message.
126 Runtime error — keychain access failure, key file not found, an empty/whitespace-only resolved secret, signing error, or output write failure. Stderr carries the message.

Colour

mkauth output is plain (no coloured output). The --color and --no-color flags are accepted for suite consistency. NO_COLOR is respected.

  • envvault — store secrets in the OS keychain and inject them as env vars: vault:NS/KEY references in mkauth read from the same store
  • digest — HMAC and cryptographic hashing for body/payload signing schemes
  • mksecret — generate HMAC signing keys, API keys, and other secrets

See Also

  • man mkauth (after winix install man)
  • mkauth --describe for JSON metadata
Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

Version Downloads Last Updated
0.4.0 158 6/13/2026

## [0.4.0]

- Initial release.

See full changelog at https://github.com/Yortw/winix/blob/main/src/mkauth/CHANGELOG.md