crates/nornir-auth-oauth2/Cargo.toml
[package]
name = "nornir-auth-oauth2"
version = "0.1.1"
edition = "2021"
authors = ["Ignalina ApS"]
description = "OAuth 2.0 / 2.1 auth PROVIDER for the nordisk servers — verify a presented access token via RFC 7662 token introspection (SP side) and drive the service-to-service client-credentials grant + interactive authorization-code + PKCE flow. Beyond `nornir-auth-oidc`'s offline JWT-vs-JWKS: this covers opaque access tokens introspected against the IdP and the pure-OAuth2 M2M grant. Enables Entra ID / Okta / Auth0 / Google / AWS IAM Identity Center as OAuth providers. REUSES the `oauth2` crate (does NOT hand-roll the protocol). Implements the shared `nornir_service::AuthProvider` seam via `verify_bearer`. Feature-gated sub-crate: the HTTP-egress (`reqwest`) closure is linked ONLY when a tier pins `online` — the offline/mini bearer tier links none of it. Consumed by holger (reference impl), korp, dwarves."
license = "MIT OR Apache-2.0"
repository = "https://git.gunnar.rs/nordisk/edda"
keywords = ["auth", "oauth2", "pkce", "introspection", "sso"]
categories = ["authentication", "network-programming"]
[lib]
name = "nornir_auth_oauth2"
[features]
default = []
# `online` — compile the outbound-HTTP paths (RFC 7662 introspection call +
# the client-credentials token exchange), reusing the `oauth2` crate's reqwest
# transport (rustls, no OpenSSL). OMITTED by the airgap/mini tier so NO
# HTTP-egress client is linked; `verify_bearer` then fails closed (`Ok(None)` —
# "not mine", the dispatcher tries the next provider). The offline
# `authorize_url_pkce` helper (pure URL construction) stays available regardless.
online = ["dep:reqwest", "oauth2/reqwest", "oauth2/rustls-tls"]
[dependencies]
# The ONE shared auth-provider seam (`AuthProvider` + `Principal`). Requires the
# 0.1.4 seam. No crypto/HTTP of our own — the protocol lives in `oauth2`.
nornir-service = { version = "0.1.4", path = "../nornir-service", features = ["auth-provider"] }
tonic = { version = "0.14", default-features = false }
# REUSE the strongly-typed OAuth2 implementation (Law #1/#5 — do NOT hand-roll
# the protocol). Core is pure Rust (url + serde, no network); the reqwest
# transport rides only behind our `online` feature so the offline tier links no
# HTTP-egress client.
oauth2 = { version = "5", default-features = false }
log = "0.4"
# Online only: the pooled HTTP client the `oauth2` transport uses for the
# introspection + token endpoints. rustls (no OpenSSL), matching the servers.
# Stays on reqwest 0.12 (2026-10-02): the `Client` built here is handed to the
# `oauth2` crate's `request_async`, and oauth2 5.0.0 (newest) implements its HTTP
# client trait for reqwest ^0.12 only. Move with oauth2.
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls"], optional = true }
[dev-dependencies]
tokio = { version = "1", features = ["macros", "rt"] }
serde_json = "1"