Skip to content

Commit

Permalink
Merge pull request #195 from sebadob/prepare-release-rauthy-client-0_1_1
Browse files Browse the repository at this point in the history
rauthy-client: provide better / complete documentation for docs.rs
  • Loading branch information
sebadob authored Nov 27, 2023
2 parents 0547e0e + 89acb68 commit 462f2d3
Show file tree
Hide file tree
Showing 13 changed files with 75 additions and 35 deletions.
2 changes: 1 addition & 1 deletion rauthy-client/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rauthy-client"
version = "0.1.0"
version = "0.1.1"
edition = "2021"
authors = ["Sebastian Dobe <[email protected]>"]
license = "Apache-2.0"
Expand Down
3 changes: 0 additions & 3 deletions rauthy-client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,5 @@ You can of course use any generic OIDC client with [Rauthy](https://github.com/s
However, the idea of this crate is to provide the simplest possible production ready setup, with the least amount of
overhead and secure default values, if you only use [Rauthy](https://github.com/sebadob/rauthy) anyway.

This client should work without any issues with other OIDC providers as well, as long as they support the S256 PKCE
flow. However, this was only tested against [Rauthy](https://github.com/sebadob/rauthy).

You can find examples for `actix-web`, `axum` or a fully generic framework / application in the
[Examples](https://github.com/sebadob/rauthy/tree/main/rauthy-client/examples) directory.
2 changes: 1 addition & 1 deletion rauthy-client/examples/actix-web/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion rauthy-client/examples/axum/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion rauthy-client/examples/generic/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions rauthy-client/examples/generic/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ async fn main() -> anyhow::Result<()> {
// If set to 'false', tokens with a non-verified email address will be rejected.
email_verified: true,
// The issuer URL from your Rauthy deployment
iss: "https://iam.sebastiandobe.de/auth/v1".to_string(),
// iss: "https://rauthy.local/auth/v1".to_string(),
iss: "https://rauthy.local/auth/v1".to_string(),
// The scopes you want to request. The only mandatory which always needs to exist is
// `openid`, the rest is optional and depending on your needs.
scope: vec![
Expand Down
2 changes: 2 additions & 0 deletions rauthy-client/src/cookie_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use tracing::error;

/// The name of the encrypted OIDC state cookie during the login flow
#[allow(dead_code)]
pub static OIDC_STATE_COOKIE: &str = "OIDC_STATE";

/// The encrypted OIDC state cookie during the login flow
#[derive(Serialize, Deserialize)]
pub struct OidcCookieState {
pub nonce: String,
Expand Down
13 changes: 9 additions & 4 deletions rauthy-client/src/handler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ pub mod axum;
#[cfg(feature = "actix-web")]
pub mod actix_web;

/// Query params appended to the callback URL after a successful login from the user
#[derive(Debug, Deserialize)]
#[allow(dead_code)]
pub struct OidcCallbackParams {
code: String,
state: String,
pub code: String,
pub state: String,
}

#[derive(Debug, Serialize)]
Expand All @@ -28,14 +29,14 @@ struct OidcCodeRequestParams {
redirect_uri: String,
}

// Used instead of a normal bool to never confuse people about a meaning of multiple bool's
/// Used instead of a normal bool to never confuse people about a meaning of multiple bool's
#[derive(Debug, PartialEq)]
pub enum OidcCookieInsecure {
Yes,
No,
}

// Used instead of a normal bool to never confuse people about a meaning of multiple bool's
/// Used instead of a normal bool to never confuse people about a meaning of multiple bool's
#[derive(Debug, PartialEq)]
pub enum OidcSetRedirectStatus {
Yes,
Expand Down Expand Up @@ -65,6 +66,8 @@ impl OidcCodeRequestParams {

/// Check the authentication
///
/// This will only exist without `actix-web` or `axum` features
///
/// # Returns
/// - Ok(()) if the user is logged in
/// - Err(None) if the user is not logged in and the OIDC provider is not correctly set up
Expand Down Expand Up @@ -107,6 +110,8 @@ pub async fn validate_principal_generic(

/// Handles the OIDC callback
///
/// If you use `actix-web` or `axum` features, you should use the more specific implementations.
///
/// # Panics
/// If the given `enc_key` is not exactly 32 bytes long
#[allow(dead_code)]
Expand Down
38 changes: 26 additions & 12 deletions rauthy-client/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
//! Minimal and safe by default client library for the [Rauthy](https://github.com/sebadob/rauthy)
//! project.
//!
//! You can of course use any generic OIDC client with [Rauthy](https://github.com/sebadob/rauthy).
//! However, the idea of this crate is to provide the simplest possible production ready setup,
//! with the least amount of overhead and secure default values, if you only use
//! [Rauthy](https://github.com/sebadob/rauthy) anyway.
//!
//! This client should work without any issues with other OIDC providers as well, as long as they
//! support the S256 PKCE flow. However, this was only tested against
//! [Rauthy](https://github.com/sebadob/rauthy).
//!
//! You can find examples for `actix-web`, `axum` or a fully generic framework / application in the
//! [Examples](https://github.com/sebadob/rauthy/tree/main/rauthy-client/examples) directory.

use crate::provider::OidcProvider;
use base64::{engine, engine::general_purpose, Engine as _};
use rand::{distributions, Rng};
Expand All @@ -7,41 +22,40 @@ use tracing::{error, warn};
use crate::jwks::jwks_handler;
pub use reqwest::Certificate as RootCertificate;

/// Handles the encrypted OIDC state cookie for the login flow
pub mod cookie_state;
/// The handlers which need to be called from your endpoints
pub mod handler;
pub mod jwks;
mod jwks;
/// The Rauthy OIDC config
pub mod oidc_config;
/// The authenticated Principal, extracted from valid JWT tokens
pub mod principal;
/// Rauthy / OIDC provider config and setup
pub mod provider;
/// Provides everything necessary to extract and validate JWT token claims
pub mod token_set;

pub(crate) const VERSION: &str = env!("CARGO_PKG_VERSION");

const B64_URL_SAFE_NO_PAD: engine::GeneralPurpose = general_purpose::URL_SAFE_NO_PAD;

#[derive(Debug, PartialEq, Eq)]
pub enum CacheMethod {
Get,
Set,
Exit,
}

/// Decodes a base64 value
#[allow(dead_code)]
pub fn b64_decode(value: &str) -> anyhow::Result<Vec<u8>> {
pub(crate) fn b64_decode(value: &str) -> anyhow::Result<Vec<u8>> {
let b = general_purpose::STANDARD.decode(value)?;
Ok(b)
}

/// Returns the given input as a base64 encoded String
#[inline]
pub fn b64_encode(value: &[u8]) -> String {
pub(crate) fn b64_encode(value: &[u8]) -> String {
general_purpose::STANDARD.encode(value)
}

/// Returns the given input as a base64 URL encoded String
#[inline]
pub fn base64_url_encode(input: &[u8]) -> String {
pub(crate) fn base64_url_encode(input: &[u8]) -> String {
let b64 = general_purpose::STANDARD.encode(input);
b64.chars()
.filter_map(|c| match c {
Expand All @@ -53,7 +67,7 @@ pub fn base64_url_encode(input: &[u8]) -> String {
.collect()
}

pub fn base64_url_no_pad_decode(b64: &str) -> anyhow::Result<Vec<u8>> {
pub(crate) fn base64_url_no_pad_decode(b64: &str) -> anyhow::Result<Vec<u8>> {
B64_URL_SAFE_NO_PAD
.decode(b64)
.map_err(|_| anyhow::Error::msg("B64 decoding error"))
Expand Down
13 changes: 13 additions & 0 deletions rauthy-client/src/oidc_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,26 @@ use std::collections::HashSet;
/// The configuration for the Rauthy setup.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RauthyConfig {
/// If this is Some(_), the principal will have a .is_admin field being set correctly, if
/// this claim matches.
pub admin_claim: Option<JwtClaim>,
/// This claim must always exist for every single user. Without this claim, a user would
/// not have access to this app. This is used, because usually you never want to just have
/// all your OIDC users to have access to a certain application.
pub user_claim: JwtClaim,
/// In almost all cases, this should just match the `client_id`
pub allowed_audiences: HashSet<String>,
/// the `client_id` from Rauthy
pub client_id: String,
/// If set to 'false', tokens with a non-verified email address will be rejected.
pub email_verified: bool,
/// The issuer URL from your Rauthy deployment
pub iss: String,
/// The scopes you want to request. The only mandatory which always needs to exist is
/// `openid`, the rest is optional and depending on your needs.
pub scope: Vec<String>,
/// If set to None, the client will be treated as a public client and not provide any
/// secret to the /token endpoint after the callback. Set a secret for confidential clients.
pub secret: Option<String>,
}

Expand Down
12 changes: 12 additions & 0 deletions rauthy-client/src/principal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,32 @@ mod axum;
/// The AuthorizedUser making requests to the API
#[derive(Debug)]
pub struct PrincipalOidc {
/// Matches the `uid` token claim
pub id: String,
/// Matches the `expires_at` token claim -> UNIX timestamp in seconds
pub expires_at_ts: Option<u64>,
/// Rauthy always set's the users email as the `preferred_username`
pub preferred_username: Option<String>,
/// Matches the `sub` token claim
pub sub: String,
/// Matches the `roles` token claim
pub roles: Vec<String>,
/// Matches the `groups` token claim
pub groups: Vec<String>,
/// Matches the `scope` token claim
pub scope: String,
/// Will be true, if the access token matches the `RauthyConfig.admin_claim`
pub is_admin: bool,
/// Will be true, if the access token matches the `RauthyConfig.user_claim`
pub is_user: bool,
/// Contains all custom scopes that are configured inside Rauthy and are mapped into the
/// `access_token` for the given user
pub custom_claims: Option<HashMap<String, serde_json::Value>>,
}

impl PrincipalOidc {
/// Creates a Principal from a raw Base64 encoded JWT token.
/// This will also validate the token against the JWK fetched from the issuer.
pub async fn from_token_validated(token: &str) -> anyhow::Result<Self> {
let claims = JwtAccessClaims::from_token_validated(token).await?;

Expand Down
8 changes: 5 additions & 3 deletions rauthy-client/src/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ pub(crate) static HTTP_CLIENT: OnceLock<reqwest::Client> = OnceLock::new();
static OIDC_CONFIG: OnceLock<OidcProviderConfig> = OnceLock::new();

#[derive(Debug)]
pub struct OidcProviderConfig {
pub(crate) struct OidcProviderConfig {
pub auth_url_base: String,
pub client_id: String,
pub email_verified: bool,
pub oidc_config_url: String,
pub provider: OidcProvider,
pub redirect_uri: String,
pub secret: Option<String>,
Expand Down Expand Up @@ -63,7 +62,6 @@ impl OidcProviderConfig {
auth_url_base,
client_id,
email_verified,
oidc_config_url,
provider,
redirect_uri,
secret,
Expand All @@ -74,6 +72,7 @@ impl OidcProviderConfig {
}
}

/// The configured Rauthy OIDC Provider
#[allow(dead_code)]
#[derive(Debug, Serialize, Deserialize)]
pub struct OidcProvider {
Expand Down Expand Up @@ -174,6 +173,7 @@ impl OidcProvider {
}
}

/// Rauthy-supported token algorithms
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Algorithm {
RS256,
Expand All @@ -182,13 +182,15 @@ pub enum Algorithm {
EdDSA,
}

/// Rauthy-supported PKCE challenges
#[allow(non_camel_case_types)]
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Challenge {
plain,
S256,
}

/// OIDC login flows
#[allow(non_camel_case_types)]
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Flows {
Expand Down
10 changes: 3 additions & 7 deletions rauthy-client/src/token_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use jwt_simple::claims;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// The token set returned upon a successful login flow
#[derive(Debug, Serialize, Deserialize)]
pub struct OidcTokenSet {
pub access_token: String,
Expand All @@ -15,6 +16,7 @@ pub struct OidcTokenSet {
pub refresh_token: Option<String>,
}

/// Rauthy-supported token types. This client however does currently not support `DPoP`.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum JwtTokenType {
Bearer,
Expand Down Expand Up @@ -117,13 +119,7 @@ pub struct JwtRefreshClaims {
pub cnf: Option<JktClaim>,
}

#[derive(Debug, PartialEq, Eq, Deserialize)]
pub enum JwtType {
Bearer,
Id,
Refresh,
}

/// Will be set to JwtAmrValue::Mfa if the user provided at least a second factor during login
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all(serialize = "lowercase"))]
pub enum JwtAmrValue {
Expand Down

0 comments on commit 462f2d3

Please sign in to comment.