#![cfg_attr(docsrs, feature(doc_cfg))]
#![forbid(unsafe_code)]
#![warn(missing_docs)]
#![cfg_attr(
feature = "http",
doc = r#"
# Quick start
Four things a host owns, and none of them can be defaulted: a config, a store, a sweep, and the
two seams the interactive endpoints refuse without.
```no_run
use std::sync::Arc;
use std::time::{Duration, SystemTime};
use oauth_as::{
AuthorizationServer, ConsentDecision, MemoryStorage, ServerConfig, ServiceBuilder, Storage,
};
# fn wire() -> Result<(), Box<dyn std::error::Error>> {
// The issuer identity, and where a user goes to type an RFC 8628 device code.
let config = ServerConfig::new("https://as.example.com", "https://as.example.com/device");
// MemoryStorage is single process. A multi-node host implements `Storage` itself and proves
// its `take_*` really is an atomic remove-and-return with the `test-util` harness.
let server = Arc::new(AuthorizationServer::new(config, MemoryStorage::new()));
// THE SWEEP. Nothing in this crate reclaims an expired record; this task is the only thing
// that does, and the device authorization endpoint takes no credential, so an unswept store
// grows at a rate an attacker chooses.
let sweeper = Arc::clone(&server);
tokio::spawn(async move {
loop {
let _ = sweeper.store().sweep_expired(SystemTime::now()).await;
tokio::time::sleep(Duration::from_secs(60)).await;
}
});
// Both seams are REQUIRED: with no consent resolver the authorization endpoint answers 403
// rather than deciding on the user's behalf. Returning `Approve` unconditionally, as here, is
// an AUTO-APPROVING authorization server (RFC 6749 s10.12); a real host reads its own session
// here and returns `ConsentDecision::Respond` with a consent screen. See
// `examples/production_server.rs` for both done properly, plus CSRF and audit.
let service = ServiceBuilder::new(server)
.with_subject_resolver(|_headers| Some("user-1".to_string()))
.with_consent_resolver(|_request| ConsentDecision::Approve)
.build()?;
# let _ = service;
# Ok(())
# }
```
"#
)]
pub mod authorization;
pub mod client;
#[cfg(feature = "client_assertion")]
#[cfg_attr(docsrs, doc(cfg(feature = "client_assertion")))]
pub mod client_assertion;
#[cfg(feature = "consent")]
#[cfg_attr(docsrs, doc(cfg(feature = "consent")))]
pub mod consent;
pub mod device;
#[cfg(feature = "dpop")]
#[cfg_attr(docsrs, doc(cfg(feature = "dpop")))]
pub mod dpop;
pub mod error;
pub mod events;
pub mod grant;
mod hex;
#[cfg(feature = "http")]
#[cfg_attr(docsrs, doc(cfg(feature = "http")))]
pub mod http;
#[cfg(feature = "jwt")]
#[cfg_attr(docsrs, doc(cfg(feature = "jwt")))]
pub mod jwt;
pub mod metadata;
#[cfg(feature = "mtls")]
#[cfg_attr(docsrs, doc(cfg(feature = "mtls")))]
pub mod mtls;
#[cfg(any(feature = "par", feature = "jar"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "par", feature = "jar"))))]
pub mod par;
pub mod pkce;
#[cfg(feature = "rar")]
#[cfg_attr(docsrs, doc(cfg(feature = "rar")))]
pub mod rar;
pub mod rate_limit;
pub mod registration;
#[cfg(feature = "resource-metadata")]
#[cfg_attr(docsrs, doc(cfg(feature = "resource-metadata")))]
pub mod resource_metadata;
pub mod scope;
pub mod server;
#[cfg(all(feature = "test-util", feature = "jwt"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "test-util", feature = "jwt"))))]
pub mod signer_conformance;
#[cfg(any(feature = "client_assertion", feature = "dpop"))]
mod skew;
#[cfg(feature = "test-util")]
#[cfg_attr(docsrs, doc(cfg(feature = "test-util")))]
pub mod storage_conformance;
pub mod store;
pub mod token;
#[cfg(feature = "token-exchange")]
#[cfg_attr(docsrs, doc(cfg(feature = "token-exchange")))]
pub mod token_exchange;
pub use authorization::{
AuthorizationCodeRecord, AuthorizationCodeState, AuthorizationError,
AuthorizationErrorRedirect, AuthorizationRequest, AuthorizationResponse, CodeChallengeMethod,
ResponseType, ValidatedAuthorizationRequest,
};
pub use client::{Client, ClientAuth, ClientId, DynamicRegistration, SecretHash, SecretVerifier};
#[cfg(feature = "client_assertion")]
#[cfg_attr(docsrs, doc(cfg(feature = "client_assertion")))]
pub use client_assertion::{
AssertionFailure, AssertionKeys, VerifiedAssertion, CLIENT_ASSERTION_TYPE, CLIENT_SECRET_JWT,
PRIVATE_KEY_JWT,
};
#[cfg(feature = "consent")]
#[cfg_attr(docsrs, doc(cfg(feature = "consent")))]
pub use consent::{
step_up_challenge, Authentication, AuthenticationRequirement, ConsentRecord, StepUpFailure,
MAX_CONSENT_RESOURCES,
};
pub use device::{DeviceAuthorizationResponse, DeviceGrant, DeviceGrantState};
#[cfg(feature = "dpop")]
#[cfg_attr(docsrs, doc(cfg(feature = "dpop")))]
pub use dpop::{DpopFailure, VerifiedProof, DPOP_HEADER, DPOP_TOKEN_TYPE, MAX_PROOF_BYTES};
pub use error::{ErrorCode, ErrorResponse};
pub use events::{
Attempt, AttemptOutcome, ClientAuthFailure, Event, EventSink, Hooks, RateLimitDecision,
RateLimiter,
};
pub use grant::GrantType;
#[cfg(feature = "http")]
#[cfg_attr(docsrs, doc(cfg(feature = "http")))]
pub use http::{
AuthorizationService, Body, ConsentDecision, ConsentRequest, ConsentResolver, CsrfTokenHook,
ServiceBuilder, ServiceError, SubjectResolver, MAX_BODY_BYTES, MAX_FORM_PARAMETERS,
};
pub use metadata::{well_known_path, AuthorizationServerMetadata, WELL_KNOWN_PATH};
#[cfg(feature = "mtls")]
#[cfg_attr(docsrs, doc(cfg(feature = "mtls")))]
pub use mtls::{
CertificateThumbprint, ClientCertificate, ExpectedSubject, MtlsClientRegistration,
MtlsRegistrationError, RegisteredCertificates, SELF_SIGNED_TLS_CLIENT_AUTH, TLS_CLIENT_AUTH,
TLS_CLIENT_AUTH_SAN_DNS, TLS_CLIENT_AUTH_SAN_EMAIL, TLS_CLIENT_AUTH_SAN_IP,
TLS_CLIENT_AUTH_SAN_URI, TLS_CLIENT_AUTH_SUBJECT_DN,
};
#[cfg(feature = "jar")]
#[cfg_attr(docsrs, doc(cfg(feature = "jar")))]
pub use par::{
JarConfig, RegisteredRequestObjectKey, RequestObjectAlg, RequestObjectKeyError,
RequestObjectKeys, REQUEST_OBJECT_SIGNING_ALGS, REQUEST_OBJECT_TYP,
};
#[cfg(feature = "par")]
#[cfg_attr(docsrs, doc(cfg(feature = "par")))]
pub use par::{
ParConfig, PushedAuthorizationRequest, PushedAuthorizationResponse, REQUEST_URI_PREFIX,
};
#[cfg(feature = "rar")]
#[cfg_attr(docsrs, doc(cfg(feature = "rar")))]
pub use rar::{
AuthorizationDetail, AuthorizationDetails, MAX_AUTHORIZATION_DETAILS_BYTES,
MAX_AUTHORIZATION_DETAILS_DEPTH, MAX_AUTHORIZATION_DETAILS_ELEMENTS,
};
pub use rate_limit::{FixedWindowRateLimiter, RateLimitConfig};
pub use registration::{
ClientInformation, ClientMetadata, RegistrationAttempt, RegistrationConfig,
RegistrationDecision, RegistrationErrorCode, RegistrationErrorResponse, RegistrationFailure,
RegistrationPolicy, MAX_REGISTERED_REDIRECT_URIS,
};
#[cfg(feature = "resource-metadata")]
#[cfg_attr(docsrs, doc(cfg(feature = "resource-metadata")))]
pub use resource_metadata::{
BearerMethod, ProtectedResourceConfig, ProtectedResourceMetadata,
PROTECTED_RESOURCE_WELL_KNOWN_PATH,
};
pub use scope::{Scope, ScopeSet};
pub use server::{
AuthorizationServer, ClientCredential, Clock, DeviceApprovalError, ServerConfig, SystemClock,
TokenRequest, TokenRequestContext, UserApproval, MAX_RESOURCE_INDICATORS, MIN_USER_CODE_LENGTH,
};
pub use store::{MemoryStorage, Storage, StorageError};
#[cfg(any(feature = "dpop", feature = "mtls"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "dpop", feature = "mtls"))))]
pub use token::Confirmation;
pub use token::{
IntrospectionResponse, IssuedToken, RefreshTokenRecord, RefreshTokenState, TokenResponse,
TokenType, TokenTypeHint,
};
#[cfg(feature = "token-exchange")]
#[cfg_attr(docsrs, doc(cfg(feature = "token-exchange")))]
pub use token_exchange::{
ActClaim, ExchangeSemantics, ExchangedToken, TokenExchange, TokenExchangeRequest,
TokenExchangeResponse, TokenTypeIdentifier, MAX_AUDIENCE_VALUES, TOKEN_EXCHANGE_GRANT_URN,
};