# ================================================================================ # `ortie` configuration file. # # Loaded from the first valid path among $XDG_CONFIG_HOME/ortie/config.toml, # $HOME/.config/ortie/config.toml and $HOME/.ortierc. `ortie -c ` and # `ORTIE_CONFIG=` override it, both accepting a `:`-delimited list whose # first entry is the base and whose rest are deep-merged on top of it. # # Each `[accounts.]` section declares one OAuth 2.0 account, picked at # runtime with `-a ` or by `default = true`. `ortie configure` discovers # one and writes it here; this file documents every field. # ================================================================================ [accounts.example] # Mark this account as the default one, used when no `-a ` is passed. default = true # TLS provider used for the HTTPS connection to the token endpoint. # # Possible values: "auto" (default, the provider this binary was built with), # "native-tls", "rustls-aws", "rustls-ring" #tls = "rustls-ring" # ALPN identifiers offered during the TLS handshake. # # Empty by default, so no ALPN extension is sent: an OAuth 2.0 endpoint is plain # HTTPS and registers no identifier. Set `["http/1.1"]` when a TLS middlebox # refuses a handshake without ALPN. Only rustls reads it, native-tls ignores it. #alpn = ["http/1.1"] # OAuth 2.0 client credentials, as registered with your provider. # # `client-secret` is optional, PKCE-only public clients skipping it, and takes # the same shapes as every pimalaya-config secret: `raw`, `command`, `env`. client-id = "" #client-secret.raw = "secret" #client-secret.command = "pass show ortie/example" # The OAuth 2.0 grant flow run by the auth commands. # # Possible values: # "authorization-code" # browser redirect flow (default) # "device" # user-code flow (RFC 8628) # "client-credentials" # headless machine flow, secret-authenticated (RFC 6749 section 4.4) # "client-credentials-jwt" # headless machine flow, JWT-assertion-authenticated (RFC 7523 section 2.2) #grant = "authorization-code" # Credentials of the "client-credentials-jwt" grant: the private key (PKCS#8 # or PKCS#1 PEM) signing the JWT assertion, and the certificate (PEM or DER) # whose SHA-1 thumbprint rides as the `x5t` header. Both are re-read at every # mint, so a renewed certificate is picked up without a restart. #client-key = "/etc/ortie/example.key.pem" #client-certificate = "/etc/ortie/example.crt.pem" # Endpoints given by your OAuth 2.0 provider. All optional at parse time. endpoints.authorization = "" #endpoints.device-authorization = "" # required when grant = "device" endpoints.token = "" # Optional redirection endpoint, defaulting to `http://127.0.0.1:0`, whose # port the OS picks. Set it to match a redirect URI the provider pins. #endpoints.redirection = "http://localhost" # OAuth 2.0 scopes granted to the access token. scopes = [] # Proof Key for Code Exchange (RFC 7636), used by the authorization code grant # and enabled with the S256 method by default, aligning with OAuth 2.1. # # Possible values: # pkce = true # S256 (same as the default) # pkce = "s256" # explicit method # pkce = "plain" # escape hatch for servers rejecting S256 # pkce = false # disable, for servers rejecting PKCE parameters #pkce = "s256" # Extra parameters forwarded verbatim to the authorization request query, # keyed by wire parameter name with no kebab-case renaming. This is where # provider-specific options go. The device grant forwards no extras yet. # # extras.access_type = "offline" # Google: required for a refresh token # extras.prompt = "consent" # Google: force the consent screen # extras.login_hint = "user@example.com" # extras.resource = "https://api.example.com/" # RFC 8707 (Fastmail needs it) #extras.access_type = "offline" # Whether `ortie token show` refreshes an expired access token by itself, # which is what passing `--auto-refresh` on every call would do. auto-refresh = true # -------------------------------------------------------------------------------- # Storage # -------------------------------------------------------------------------------- # # Ortie persists no token itself: reads and writes go through commands of # yours. Any CLI printing on stdout and reading from stdin works, so pick one # for your platform: # # GNOME / Secret Service .... `secret-tool` (libsecret) # KDE ....................... `kwallet-query`, `kwalletcli` # macOS ..................... `security find/add-generic-password` # Windows ................... `cmdkey`, PowerShell `Get-Credential` # Headless / CI ............. `keyctl` (kernel keyutils) # Terminal-first, portable .. `pass`, `gopass` # # Every `*.command` field takes either shape. An array runs the program # directly, with no shell involved. A string goes through the platform shell # (`/bin/sh -c` on Unix, `cmd /C` on Windows), which is what pipes, globs and # `$VAR` substitution need. storage.read.command = ["pass", "show", "ortie/example"] storage.write.command = "pass insert -m -f ortie/example" # -------------------------------------------------------------------------------- # Hooks # -------------------------------------------------------------------------------- # # Each hook fires on token issuance (`on-issue`) or refresh (`on-refresh`), # split by outcome (`success` / `error`). Both accept a `command`, in the same # two shapes as the storage commands above, and a `notify` block, which needs # the `notify` cargo feature, off by default. # # A success hook receives ACCESS_TOKEN, TOKEN_TYPE, EXPIRES_IN, REFRESH_TOKEN # and SCOPE, an error hook ERROR, ERROR_DESCRIPTION and ERROR_URI. The command # expands them through the shell, so use the string shape, and the `notify` # summary and body expand them too. #hooks.on-issue.success.command = "logger 'ortie issued a token (expires in $EXPIRES_IN)'" #hooks.on-issue.success.notify.summary = "Ortie" #hooks.on-issue.success.notify.body = "Issued access token (expires in $EXPIRES_IN)" #hooks.on-issue.error.notify.summary = "Ortie" #hooks.on-issue.error.notify.body = "[$ERROR] Issue access token error\n$ERROR_DESCRIPTION" #hooks.on-refresh.success.notify.summary = "Ortie" #hooks.on-refresh.success.notify.body = "Refreshed access token (expires in $EXPIRES_IN)" #hooks.on-refresh.error.notify.summary = "Ortie" #hooks.on-refresh.error.notify.body = "[$ERROR] Refresh access token error\n$ERROR_DESCRIPTION" # -------------------------------------------------------------------------------- # Headless service accounts # -------------------------------------------------------------------------------- # # The client credentials grants run without any user interaction: `auth get` # completes in one shot, and an expired token silently re-acquires since none # of them issues a refresh token, so `ortie token show --auto-refresh` always # prints a valid one. # # Both examples are Microsoft Entra shaped, with a tenanted token endpoint and # a resource `.default` scope. # Secret-authenticated (RFC 6749 section 4.4): # #[accounts.graph-daemon] #grant = "client-credentials" #client-id = "" #client-secret.command = ["secret-tool", "lookup", "oauth", "graph-daemon"] #endpoints.token = "https://login.microsoftonline.com//oauth2/v2.0/token" #scopes = ["https://graph.microsoft.com/.default"] #storage.read.command = ["secret-tool", "lookup", "token", "graph-daemon"] #storage.write.command = "secret-tool store --label ortie token graph-daemon" # Certificate credentials (RFC 7523 section 2.2), authenticated by a JWT # assertion signed with the private key. Microsoft requires the certificate # thumbprint, so `client-certificate` is set and `client-secret` stays unset, # its Basic header conflicting with the assertion: # #[accounts.graph-cert-daemon] #grant = "client-credentials-jwt" #client-id = "" #client-key = "/etc/ortie/graph.key.pem" #client-certificate = "/etc/ortie/graph.crt.pem" #endpoints.token = "https://login.microsoftonline.com//oauth2/v2.0/token" #scopes = ["https://graph.microsoft.com/.default"] #storage.read.command = ["secret-tool", "lookup", "token", "graph-cert-daemon"] #storage.write.command = "secret-tool store --label ortie token graph-cert-daemon" # -------------------------------------------------------------------------------- # Provider recipes # -------------------------------------------------------------------------------- # # Ready-made per-provider blocks (Google, Microsoft, Microsoft Graph, Fastmail) # live in the Configuration section of the README, where they render on the # repository page: https://github.com/pimalaya/ortie#configuration