Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: implement IntoPayload for Claims #113

Merged
merged 3 commits into from
Jan 26, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 76 additions & 10 deletions src/jwt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

use alloc::string::String;

use serde::{Deserialize, Serialize};
use serde::{de::DeserializeOwned, Deserialize, Serialize};

use crate::{
format::{self, Compact},
jws::JsonWebSignatureBuilder,
JsonWebSignature, Jws,
jws::{FromRawPayload, IntoPayload, JsonWebSignatureBuilder, PayloadData, PayloadKind},
Base64UrlString, JsonWebSignature, Jws,
};

/// A JSON Web Token (JWT) as defined in [RFC 7519].
Expand Down Expand Up @@ -42,51 +42,117 @@
/// The "iss" (issuer) claim identifies the principal that issued the JWT.
///
/// As defined in [RFC 7519 Section 4.1.1](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.1).
#[serde(rename = "iss")]
#[serde(rename = "iss", skip_serializing_if = "Option::is_none")]
pub issuer: Option<String>,

/// The "sub" (subject) claim identifies the principal that is the subject
/// of the JWT.
///
/// As defined in [RFC 7519 Section 4.1.2](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.2).
#[serde(rename = "sub")]
#[serde(rename = "sub", skip_serializing_if = "Option::is_none")]
pub subject: Option<String>,

/// The "aud" (audience) claim identifies the recipients that the JWT is
/// intended for.
///
/// As defined in [RFC 7519 Section 4.1.3](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3).
#[serde(rename = "aud")]
#[serde(rename = "aud", skip_serializing_if = "Option::is_none")]
pub audience: Option<String>,

/// The "exp" (expiration time) claim identifies the expiration time on or
/// after which the JWT MUST NOT be accepted for processing.
///
/// As defined in [RFC 7519 Section 4.1.4](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4).
#[serde(rename = "exp")]
#[serde(rename = "exp", skip_serializing_if = "Option::is_none")]
pub expiration: Option<u64>,

/// The "nbf" (not before) claim identifies the time before which the JWT
/// MUST NOT be accepted for processing.
///
/// As defined in [RFC 7519 Section 4.1.5](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.5).
#[serde(rename = "nbf")]
#[serde(rename = "nbf", skip_serializing_if = "Option::is_none")]
pub not_before: Option<u64>,

/// The "iat" (issued at) claim identifies the time at which the JWT was
/// issued.
///
/// As defined in [RFC 7519 Section 4.1.6](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.6).
#[serde(rename = "iat")]
#[serde(rename = "iat", skip_serializing_if = "Option::is_none")]
pub issued_at: Option<u64>,

/// The "jti" (JWT ID) claim provides a unique identifier for the JWT.
///
/// As defined in [RFC 7519 Section 4.1.7](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.7).
#[serde(rename = "jti")]
#[serde(rename = "jti", skip_serializing_if = "Option::is_none")]
pub jwt_id: Option<String>,

/// Additional, potentially unregistered JWT claims.
#[serde(flatten)]
pub additional: A,
}

impl<A> IntoPayload for Claims<A>
where
A: Serialize,
{
type Error = serde_json::Error;

fn into_payload(self) -> Result<PayloadKind, Self::Error> {
let encoded = serde_json::to_vec(&self)?;
Ok(PayloadKind::Attached(PayloadData::Standard(
Base64UrlString::encode(encoded),
)))
}

Check warning on line 105 in src/jwt.rs

View check run for this annotation

Codecov / codecov/patch

src/jwt.rs#L100-L105

Added lines #L100 - L105 were not covered by tests
}

/// Error returned by [`FromRawPayload`] implementation of [`Claims`]
#[derive(Debug, thiserror_no_std::Error)]

Check warning on line 109 in src/jwt.rs

View check run for this annotation

Codecov / codecov/patch

src/jwt.rs#L109

Added line #L109 was not covered by tests
#[non_exhaustive]
pub enum ClaimsDecodeError {
/// [`Claims`] does not support this operation.
#[error("Operation not supported.")]
OperationUnsupported,
/// Error while deserializing underlying Json
#[error(transparent)]
Json(#[from] serde_json::Error),
}

impl<A> FromRawPayload for Claims<A>
where
A: DeserializeOwned,
{
type Context = ();
type Error = ClaimsDecodeError;

fn from_attached(_: &Self::Context, payload: PayloadData) -> Result<Self, Self::Error> {
let data = match payload {
PayloadData::Standard(data) => data.decode(),

Check warning on line 129 in src/jwt.rs

View check run for this annotation

Codecov / codecov/patch

src/jwt.rs#L127-L129

Added lines #L127 - L129 were not covered by tests
};
let claims: Claims<A> = serde_json::from_slice(&data)?;
Ok(claims)
}

Check warning on line 133 in src/jwt.rs

View check run for this annotation

Codecov / codecov/patch

src/jwt.rs#L131-L133

Added lines #L131 - L133 were not covered by tests

/// Detached is not supported with [`JsonWebToken`]
///
/// # Returns
///
/// Always returns [`ClaimsDecodeError::OperationUnsupported`]
fn from_detached<F, T>(
_: &Self::Context,
_: &crate::JoseHeader<F, T>,
) -> Result<(Self, PayloadData), Self::Error> {
Err(ClaimsDecodeError::OperationUnsupported)
}

Check warning on line 145 in src/jwt.rs

View check run for this annotation

Codecov / codecov/patch

src/jwt.rs#L140-L145

Added lines #L140 - L145 were not covered by tests

/// Detached is not supported with [`JsonWebToken`]
///
/// # Returns
///
/// Always returns [`ClaimsDecodeError::OperationUnsupported`]
fn from_detached_many<F, T>(
_: &Self::Context,
_: &[crate::JoseHeader<F, T>],
) -> Result<(Self, PayloadData), Self::Error> {
Err(ClaimsDecodeError::OperationUnsupported)
}

Check warning on line 157 in src/jwt.rs

View check run for this annotation

Codecov / codecov/patch

src/jwt.rs#L152-L157

Added lines #L152 - L157 were not covered by tests
}
Loading