Skip to content

pimalaya/ortie

🔑 Ortie Documentation Matrix Mastodon

CLI and lib to manage OAuth 2.0 tokens, written in Rust.

This repository ships three layers:

  • Low-level I/O-free coroutines: no_std-friendly state machines that emit read/write requests for any runtime.
  • Mid-level blocking client wrapping the coroutines around any Read + Write + Send stream, or (with a TLS feature on) building the TLS stream itself via pimalaya-stream.
  • High-level CLI consuming the std client, configured through TOML.

Table of contents

Features

  • OAuth 2.0 Authorization Code Grant rfc6749 #4.1 and refresh rfc6749 #6
  • PKCE rfc7636
  • TLS support:
    • Rustls with ring crypto (rustls-ring feature, default)
    • Rustls with aws crypto (requires rustls-aws feature)
    • Native TLS (requires native-tls feature)
  • Fake HTTP redirection server during the interactive flow
  • Shell command storages for reading and writing access tokens
  • Shell command hooks on success and error of token issuance / refresh
  • System notification hooks (requires notify feature)
  • JSON output via --json

Tip

Ortie is written in Rust and uses cargo features to gate optional functionality. The default feature set is declared in Cargo.toml.

Installation

Pre-built binary

Ortie can be installed with the installer:

As root:

curl -sSL https://raw.githubusercontent.com/pimalaya/ortie/master/install.sh | sudo sh

As a regular user:

curl -sSL https://raw.githubusercontent.com/pimalaya/ortie/master/install.sh | PREFIX=~/.local sh

These commands install the latest binary from the GitHub releases section.

For a more up-to-date version than the latest release, check out the releases GitHub workflow and look for the Artifacts section. These pre-built binaries are built from the master branch.

Note

Such binaries are built with the default cargo features. If you need specific features, please use another installation method.

Cargo

cargo install --locked ortie

For the git tip:

cargo install --locked --git https://github.com/pimalaya/ortie.git

Nix

If you have the Flakes feature enabled:

nix profile install github:pimalaya/ortie

Or run without installing:

nix run github:pimalaya/ortie

Sources

git clone https://github.com/pimalaya/ortie
cd ortie
nix run

Configuration

Ortie does not yet ship a wizard: copy config.sample.toml into one of the canonical paths below and edit it by hand.

A configuration is loaded from the first valid path among:

  • $XDG_CONFIG_HOME/ortie/config.toml
  • $HOME/.config/ortie/config.toml
  • $HOME/.ortierc

Override the path with -c <PATH> or ORTIE_CONFIG=<PATH>; multiple paths can be passed at once, separated by :. The first one is the base and the rest are deep-merged on top.

You will also need a registered OAuth 2.0 application: either use a public application (Thunderbird credentials cover most consumer providers) or register your own. The first option is simpler.

See public Thunderbird application credentials for various providers at github.com/mozilla.

Google

endpoints.authorization = "https://accounts.google.com/o/oauth2/auth?access_type=offline"
endpoints.token = "https://www.googleapis.com/oauth2/v3/token"
scopes = ["https://www.googleapis.com/auth/carddav", "https://mail.google.com"]

Using the public Thunderbird application:

client-id = "406964657835-aq8lmia8j95dhl1a2bvharmfk3t1hgqj.apps.googleusercontent.com"
client-secret.raw = "kSmqreRr0qwBWJgbf5Y-PjSU"
endpoints.redirection = "http://localhost"

Using your own application:

client-id = "<your-client-id>"
client-secret.raw = "<your-client-secret>"

Microsoft

endpoints.authorization = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize"
endpoints.token = "https://login.microsoftonline.com/common/oauth2/v2.0/token"

Using the public Thunderbird application:

client-id = "9e5f94bc-e8a4-4e73-b8be-63364c29d753"
endpoints.redirection = "https://localhost"

Using your own application:

client-id = "<your-client-id>"
client-secret.raw = "<your-client-secret>"

Usage

Library

The low-level coroutines live under ortie::authorization_code_grant, ortie::issue_access_token and ortie::refresh_access_token; they emit WantsRead / WantsWrite(bytes) events the caller pumps against any transport. The mid-level ortie::client::OauthClientStd inlines the loop against a single boxed stream, with two construction paths:

use ortie::{
    client::OauthClientStd,
    refresh_access_token::RefreshAccessTokenParams,
};

// Light: caller owns the connection (TCP, mock, plaintext, …).
let mut client = OauthClientStd::new(my_stream, token_endpoint, &client_id);

// Full (requires `rustls-ring` / `rustls-aws` / `native-tls`):
// opens the TLS stream itself via pimalaya-stream.
let mut client = OauthClientStd::connect(token_endpoint, &tls, &client_id)?;

let res = client.refresh_access_token(RefreshAccessTokenParams {
    client_id: client_id.into(),
    refresh_token,
    scopes,
})?;

A complete example using the authorization code grant flow lives in examples/authorization_code_grant.rs.

Request a new access token

$ ortie auth get

Created authorization request with:
 - state: RWdzST0ybUIzT1wtMSF9OCMmJHJUVmJrUmhhU0haLz4
 - pkce: oJ-rEXNu9YzqpCWVIPOwD5KvMhLAT73dstk0jye8nZ6

Sending authorization request to your browser:
https://login.example.com/oauth/authorize?…
Wait for redirection…

Follow the browser flow, then on success the terminal shows:

Access token successfully issued (expires in 1h)

If the redirection server cannot start (port permission denied, etc.), copy the URL you are redirected to and complete the flow manually:

ortie auth resume \
  --state RWdzST0ybUIzT1wtMSF9OCMmJHJUVmJrUmhhU0haLz4 \
  --pkce oJ-rEXNu9YzqpCWVIPOwD5KvMhLAT73dstk0jye8nZ6 \
  https://localhost/?code=M.C521_BAY.2.U&state=RWdzST0ybUIzT1wtMSF9OCMmJHJUVmJrUmhhU0haLz4

Refresh an access token

$ ortie token refresh

Access token successfully refreshed (expires in 1h)

Show an access token

$ ortie token show

EwA4BOl3BAAUcDnR9grBJokeAHaUV8R3+rVHX+IAAQfw9oZLztQS8bo8NvyWmbs…

The --auto-refresh flag (or the auto-refresh = true config option) automatically refreshes expired tokens.

Inspect token metadata:

$ ortie token inspect

Token type: bearer
Issued: 22h 51m 1s ago
Expires in: 52m 38s
With refresh token: true
With scope: https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/SMTP.Send

Debugging

The --log-level <LEVEL> flag controls log verbosity (off, error, warn, info, debug, trace). When omitted, RUST_LOG is consulted; it supports per-target filters (see the env_logger docs). RUST_BACKTRACE=1 enables the full error backtrace.

Logs go to stderr by default; redirect them with --log-file <PATH> or shell redirection:

ortie token show --log-level debug --log-file /tmp/ortie.log
ortie token show --log-level trace 2>/tmp/ortie.log

Alternatives

AI disclosure

This project is developed with AI assistance. This section documents how, so users and downstream packagers can make informed decisions.

  • Tools: Claude Code (Anthropic), Opus 4.7, invoked locally with a persistent project-scoped memory and a small set of repo-specific rules.

  • Used for: Refactors, mechanical multi-file edits, boilerplate (feature gates, error enums, derive macros, trait impls), test scaffolding, doc polish, exploratory design conversations.

  • Not used for: Engineering, critical code, git manipulation (commit, merge, rebase…), real-world tests.

  • Verification: Every AI-assisted change is read, compiled, tested, and formatted before commit (nix develop --command cargo check / cargo test / cargo fmt). Behavioural correctness is verified against the relevant RFC or upstream spec, not assumed from the model output. Tests are never adjusted to fit AI-generated code; the code is adjusted to fit correct behaviour.

  • Limitations: AI models occasionally produce code that compiles and passes tests but is subtly wrong: off-by-one errors, missed edge cases, plausible but nonexistent APIs, stale RFC references. The verification workflow catches most of this; it does not catch all of it. Bug reports are welcome and taken seriously.

  • Last reviewed: 30/05/2026

Social

Sponsoring

nlnet

Special thanks to the NLnet foundation and the European Commission that have been financially supporting the project for years:

If you appreciate the project, feel free to donate using one of the following providers:

GitHub Ko-fi Buy Me a Coffee Liberapay thanks.dev PayPal

About

CLI and lib to manage OAuth 2.0 tokens, written in Rust

Topics

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Contributing

Security policy

Stars

Watchers

Forks

Contributors