Winix.MkAuth
0.4.0
Prefix Reserved
dotnet tool install --global Winix.MkAuth --version 0.4.0
dotnet new tool-manifest
dotnet tool install --local Winix.MkAuth --version 0.4.0
#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 +--paramparams +oauth_*params (excludingrealmandoauth_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.
Related Tools
envvault— store secrets in the OS keychain and inject them as env vars:vault:NS/KEYreferences inmkauthread from the same storedigest— HMAC and cryptographic hashing for body/payload signing schemesmksecret— generate HMAC signing keys, API keys, and other secrets
See Also
man mkauth(afterwinix install man)mkauth --describefor JSON metadata
| Product | Versions 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. |
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