OAuth2Client

Struct OAuth2Client 

Source
pub struct OAuth2Client {
    pub provider_config: ProviderConfig,
    /* private fields */
}
Expand description

OAuth 2.1 client wrapper supporting all modern flows

Fields§

§provider_config: ProviderConfig

Provider-specific configuration

Implementations§

Source§

impl OAuth2Client

Source

pub fn new( config: &OAuth2Config, provider_type: ProviderType, ) -> McpResult<Self>

Create an OAuth 2.1 client supporting all flows

Source

pub fn auth_code_client(&self) -> &BasicClient

Get access to the authorization code client

Source

pub fn client_credentials_client(&self) -> Option<&BasicClient>

Get access to the client credentials client (if available)

Source

pub fn device_code_client(&self) -> Option<&BasicClient>

Get access to the device code client (if available)

Source

pub fn provider_config(&self) -> &ProviderConfig

Get the provider configuration

Source

pub fn authorization_code_flow( &self, scopes: Vec<String>, state: String, ) -> (String, String)

Start authorization code flow with PKCE

This initiates the OAuth 2.1 authorization code flow with PKCE (RFC 7636) for enhanced security, especially for public clients.

§PKCE Code Verifier Storage (CRITICAL SECURITY REQUIREMENT)

The returned code_verifier MUST be securely stored and associated with the state parameter until the authorization code is exchanged for tokens.

Storage Options (from most to least secure):

  1. Server-side encrypted session (RECOMMENDED for web apps)

    • Store in server session with HttpOnly, Secure, SameSite=Lax cookies
    • Associate with state parameter for CSRF protection
    • Automatic cleanup after exchange or timeout
  2. Redis/Database with TTL (RECOMMENDED for distributed systems)

    • Key: state parameter, Value: encrypted code_verifier
    • Set TTL to match authorization timeout (typically 10 minutes)
    • Use server-side encryption at rest
  3. In-memory for SPAs (ACCEPTABLE for public clients only)

    • Store in JavaScript closure or React state (NOT localStorage/sessionStorage)
    • Clear immediately after token exchange
    • Risk: XSS can steal verifier

NEVER:

  • Store in localStorage or sessionStorage (XSS risk)
  • Send to client in URL or query parameters
  • Log or expose in error messages
§Arguments
  • scopes - Requested OAuth scopes
  • state - CSRF protection state parameter (use cryptographically random value)
§Returns

Tuple of (authorization_url, PKCE code_verifier for secure storage)

§Example
// Server-side web app (RECOMMENDED)
let state = generate_csrf_token();  // Cryptographically random
let (auth_url, code_verifier) = client.authorization_code_flow(scopes, state.clone());

// Store securely server-side
session.insert("oauth_state", state);
session.insert("pkce_verifier", code_verifier);  // Encrypted session

// Redirect user
redirect_to(auth_url);
Source

pub async fn exchange_code_for_token( &self, code: String, code_verifier: String, ) -> McpResult<TokenInfo>

Exchange authorization code for access token

This exchanges the authorization code received from the OAuth provider for an access token using PKCE (RFC 7636).

§Arguments
  • code - Authorization code from OAuth provider
  • code_verifier - PKCE code verifier (from authorization_code_flow)
§Returns

TokenInfo containing access token and refresh token (if available)

Source

pub async fn refresh_access_token( &self, refresh_token: &str, ) -> McpResult<TokenInfo>

Refresh an access token with automatic refresh token rotation

This uses a refresh token to obtain a new access token without requiring user interaction. OAuth 2.1 and RFC 9700 recommend refresh token rotation where the server issues a new refresh token with each refresh request.

§Refresh Token Rotation (OAuth 2.1 / RFC 9700 Best Practice)

When the server supports rotation:

  • A new refresh token is returned in the response
  • The old refresh token should be discarded immediately
  • Store and use the new refresh token for future requests
  • This prevents token theft detection

Important: Always check if token_info.refresh_token is present in the response. If present, you MUST replace your stored refresh token with the new one. If absent, continue using the current refresh token.

§Arguments
  • refresh_token - The current refresh token
§Returns

New TokenInfo with:

  • Fresh access token (always present)
  • New refresh token (if server supports rotation)
§Example
let mut stored_refresh_token = "current_refresh_token";
let new_tokens = client.refresh_access_token(stored_refresh_token).await?;

// Check for refresh token rotation
if let Some(new_refresh_token) = &new_tokens.refresh_token {
    // Server rotated the token - update storage
    stored_refresh_token = new_refresh_token;
    println!("Refresh token rotated (security best practice)");
}
// Use new access token
let access_token = new_tokens.access_token;
Source

pub async fn client_credentials_flow( &self, scopes: Vec<String>, ) -> McpResult<TokenInfo>

Client credentials flow for server-to-server authentication

This implements the OAuth 2.1 Client Credentials flow for service-to-service communication without user involvement.

§Arguments
  • scopes - Requested OAuth scopes
§Returns

TokenInfo with access token (typically without refresh token)

Source

pub async fn revoke_token(&self, token_info: &TokenInfo) -> McpResult<()>

Revoke a token using RFC 7009 Token Revocation

Per RFC 7009 Section 2, prefer revoking refresh tokens (which MUST be supported by the server if issued) over access tokens (which MAY be supported).

§Arguments
  • token_info - Token information containing access and/or refresh token
§Returns

Ok if revocation succeeded or token was already invalid (per RFC 7009)

§Errors

Returns error if:

  • No revocation endpoint was configured
  • Network/HTTP error occurred
  • Server returned an error response
Source

pub fn is_token_expired(&self, token: &TokenInfo) -> bool

Validate that an access token is still valid

This checks if a token has expired based on expiration time. Note: This is a client-side check only; servers may have revoked the token.

Trait Implementations§

Source§

impl Clone for OAuth2Client

Source§

fn clone(&self) -> OAuth2Client

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for OAuth2Client

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more