Struct AuthService

Source
pub struct AuthService {
    pub jwt_config: JwtConfig,
}
Expand description

Authentication service that handles ECDSA challenge-based authentication

This service provides stateless authentication operations:

  • Challenge generation for clients to sign
  • Signature verification against challenges
  • JWT token creation for authenticated sessions
  • JWT token validation

The service does not store any state - developers must handle challenge storage and session management in their own systems.

§Example

use ecdsa_jwt::{AuthService, JwtConfig};
use secrecy::Secret;
use base64::prelude::*;

let config = JwtConfig {
    secret: Secret::new(BASE64_STANDARD.encode("your-secret")),
    ttl: 3600, // 1 hour
};
let auth_service = AuthService::new(config);

Fields§

§jwt_config: JwtConfig

Implementations§

Source§

impl AuthService

Source

pub fn new(jwt_config: JwtConfig) -> Self

Create a new authentication service with the given JWT configuration

§Arguments
  • jwt_config - Configuration containing JWT secret and token lifetime
§Example
use ecdsa_jwt::{auth::{AuthService},config::JwtConfig};
use secrecy::Secret;
use base64::prelude::*;

let config = JwtConfig {  
    secret: Secret::new(BASE64_STANDARD.encode("your-secret")),
    ttl: 3600, // 1 hour
};
let auth_service = AuthService::new(config);
Examples found in repository?
examples/basic_workflow.rs (line 19)
11fn main() -> Result<(), Box<dyn std::error::Error>> {
12    println!("ECDSA-JWT Basic Usage Example");
13
14    // 1. Setup authentication service
15    let jwt_config = JwtConfig {
16        secret: Secret::new(BASE64_STANDARD.encode("example-secret-key")),
17        ttl: 3600, // 1 hour
18    };
19    let auth_service = AuthService::new(jwt_config);
20
21    // 2. Simulate challenge storage (in real app, use Redis/DB)
22    let mut challenges: HashMap<String, String> = HashMap::new();
23
24    // 3. Generate challenge
25    let challenge = auth_service.generate_challenge();
26    let session_id = "example-session-123";
27    challenges.insert(session_id.to_string(), challenge.clone());
28
29    println!("Generated challenge: {challenge}");
30    println!("Stored with session ID: {session_id}");
31
32    // 4. Simulate client authentication (this would normally fail without real signature)
33    println!("\n Authentication attempt...");
34
35    let auth_request = AuthRequest {
36        challenge,
37        signature: "dummy-signature-for-example".to_string(),
38        public_key: "-----BEGIN PUBLIC KEY-----\nDummyKeyForExample\n-----END PUBLIC KEY-----"
39            .to_string(),
40    };
41
42    // This will fail, but demonstrates the API structure
43    match auth_service.authenticate(auth_request, false) {
44        Ok(response) => {
45            println!("Authentication successful!");
46            println!("   JWT Token: {}", response.session_token);
47            println!("   Session ID: {}", response.session_id);
48            println!("   Expires at: {}", response.expires_at);
49        }
50        Err(e) => {
51            println!(" Authentication failed (expected with dummy data): {e}");
52            println!("   In real usage, provide valid signature and public key.");
53        }
54    }
55
56    // 5. Demonstrate JWT validation with a real token
57    println!("\nJWT Token Operations...");
58
59    let session_id = uuid::Uuid::new_v4();
60    let jwt_token = ecdsa_jwt::crypto::jwt::create_jwt(session_id, None, &auth_service.jwt_config)?;
61
62    println!("Created JWT: {}...", &jwt_token[..50]);
63
64    match auth_service.validate_session(&jwt_token) {
65        Ok(claims) => {
66            println!("Token validation successful!");
67            println!("User/Session ID: {}", claims.sub);
68            println!("Issued at: {}", claims.iat);
69            println!("Expires at: {}", claims.exp);
70        }
71        Err(e) => {
72            println!("Token validation failed: {e}");
73        }
74    }
75
76    println!("\nExample completed! Check the documentation for real implementation details.");
77    Ok(())
78}
Source

pub fn generate_challenge(&self) -> String

Generate a cryptographically secure random challenge

Creates a 32-byte random challenge encoded as base64. This challenge should be:

  1. Sent to the client
  2. Stored temporarily by the server (with expiration)
  3. Signed by the client using their private key
  4. Submitted back with the signature for verification
§Returns

Base64-encoded 32-byte random challenge

§Example
use ecdsa_jwt::{auth::{AuthService},config::JwtConfig};
use secrecy::Secret;
use base64::prelude::*;

let config = JwtConfig {  
    secret: Secret::new(BASE64_STANDARD.encode("your-secret")),
    ttl: 3600, // 1 hour
};
let auth_service = AuthService::new(config);
let challenge = auth_service.generate_challenge();
// Store this challenge with a session ID and expiration time
// Send challenge to client for signing
Examples found in repository?
examples/basic_workflow.rs (line 25)
11fn main() -> Result<(), Box<dyn std::error::Error>> {
12    println!("ECDSA-JWT Basic Usage Example");
13
14    // 1. Setup authentication service
15    let jwt_config = JwtConfig {
16        secret: Secret::new(BASE64_STANDARD.encode("example-secret-key")),
17        ttl: 3600, // 1 hour
18    };
19    let auth_service = AuthService::new(jwt_config);
20
21    // 2. Simulate challenge storage (in real app, use Redis/DB)
22    let mut challenges: HashMap<String, String> = HashMap::new();
23
24    // 3. Generate challenge
25    let challenge = auth_service.generate_challenge();
26    let session_id = "example-session-123";
27    challenges.insert(session_id.to_string(), challenge.clone());
28
29    println!("Generated challenge: {challenge}");
30    println!("Stored with session ID: {session_id}");
31
32    // 4. Simulate client authentication (this would normally fail without real signature)
33    println!("\n Authentication attempt...");
34
35    let auth_request = AuthRequest {
36        challenge,
37        signature: "dummy-signature-for-example".to_string(),
38        public_key: "-----BEGIN PUBLIC KEY-----\nDummyKeyForExample\n-----END PUBLIC KEY-----"
39            .to_string(),
40    };
41
42    // This will fail, but demonstrates the API structure
43    match auth_service.authenticate(auth_request, false) {
44        Ok(response) => {
45            println!("Authentication successful!");
46            println!("   JWT Token: {}", response.session_token);
47            println!("   Session ID: {}", response.session_id);
48            println!("   Expires at: {}", response.expires_at);
49        }
50        Err(e) => {
51            println!(" Authentication failed (expected with dummy data): {e}");
52            println!("   In real usage, provide valid signature and public key.");
53        }
54    }
55
56    // 5. Demonstrate JWT validation with a real token
57    println!("\nJWT Token Operations...");
58
59    let session_id = uuid::Uuid::new_v4();
60    let jwt_token = ecdsa_jwt::crypto::jwt::create_jwt(session_id, None, &auth_service.jwt_config)?;
61
62    println!("Created JWT: {}...", &jwt_token[..50]);
63
64    match auth_service.validate_session(&jwt_token) {
65        Ok(claims) => {
66            println!("Token validation successful!");
67            println!("User/Session ID: {}", claims.sub);
68            println!("Issued at: {}", claims.iat);
69            println!("Expires at: {}", claims.exp);
70        }
71        Err(e) => {
72            println!("Token validation failed: {e}");
73        }
74    }
75
76    println!("\nExample completed! Check the documentation for real implementation details.");
77    Ok(())
78}
Source

pub fn authenticate( &self, auth_request: AuthRequest, include_public_key: bool, ) -> Result<AuthResponse>

Authenticate a client by verifying their signed challenge

This is the core authentication method that:

  1. Validates all input parameters
  2. Decodes base64-encoded challenge and signature
  3. Verifies the ECDSA signature using the provided public key
  4. Creates and returns a JWT token on successful verification
§Arguments
  • auth_request - Contains challenge, signature, and public key
§Returns
  • Ok(AuthResponse) - Authentication successful, contains JWT token
  • Err(AuthError) - Authentication failed with specific error details
§Errors
  • InvalidPublicKey - Public key is empty or malformed
  • InvalidChallenge - Challenge is empty or invalid
  • InvalidSignature - Signature is empty, malformed, or verification failed
  • Base64Error - Challenge or signature has invalid base64 encoding
  • CryptoError - Unexpected cryptographic operation failure
§Example
use ecdsa_jwt::{auth::{AuthService, AuthRequest},config::JwtConfig};
use secrecy::Secret;
use base64::prelude::*;

let config = JwtConfig {  
    secret: Secret::new(BASE64_STANDARD.encode("your-secret")),
    ttl: 3600, // 1 hour
};

let auth_request = AuthRequest {
    challenge: "stored_challenge".to_string(),
    signature: "client_signature".to_string(),
    public_key: "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----".to_string(),
};
let auth_service = AuthService::new(config);
match auth_service.authenticate(auth_request, true) { // true = include public key in JWT
    Ok(response) => {
        println!("Authentication successful!");
        println!("JWT: {}", response.session_token);
    }
    Err(e) => println!("Authentication failed: {}", e),
}
Examples found in repository?
examples/basic_workflow.rs (line 43)
11fn main() -> Result<(), Box<dyn std::error::Error>> {
12    println!("ECDSA-JWT Basic Usage Example");
13
14    // 1. Setup authentication service
15    let jwt_config = JwtConfig {
16        secret: Secret::new(BASE64_STANDARD.encode("example-secret-key")),
17        ttl: 3600, // 1 hour
18    };
19    let auth_service = AuthService::new(jwt_config);
20
21    // 2. Simulate challenge storage (in real app, use Redis/DB)
22    let mut challenges: HashMap<String, String> = HashMap::new();
23
24    // 3. Generate challenge
25    let challenge = auth_service.generate_challenge();
26    let session_id = "example-session-123";
27    challenges.insert(session_id.to_string(), challenge.clone());
28
29    println!("Generated challenge: {challenge}");
30    println!("Stored with session ID: {session_id}");
31
32    // 4. Simulate client authentication (this would normally fail without real signature)
33    println!("\n Authentication attempt...");
34
35    let auth_request = AuthRequest {
36        challenge,
37        signature: "dummy-signature-for-example".to_string(),
38        public_key: "-----BEGIN PUBLIC KEY-----\nDummyKeyForExample\n-----END PUBLIC KEY-----"
39            .to_string(),
40    };
41
42    // This will fail, but demonstrates the API structure
43    match auth_service.authenticate(auth_request, false) {
44        Ok(response) => {
45            println!("Authentication successful!");
46            println!("   JWT Token: {}", response.session_token);
47            println!("   Session ID: {}", response.session_id);
48            println!("   Expires at: {}", response.expires_at);
49        }
50        Err(e) => {
51            println!(" Authentication failed (expected with dummy data): {e}");
52            println!("   In real usage, provide valid signature and public key.");
53        }
54    }
55
56    // 5. Demonstrate JWT validation with a real token
57    println!("\nJWT Token Operations...");
58
59    let session_id = uuid::Uuid::new_v4();
60    let jwt_token = ecdsa_jwt::crypto::jwt::create_jwt(session_id, None, &auth_service.jwt_config)?;
61
62    println!("Created JWT: {}...", &jwt_token[..50]);
63
64    match auth_service.validate_session(&jwt_token) {
65        Ok(claims) => {
66            println!("Token validation successful!");
67            println!("User/Session ID: {}", claims.sub);
68            println!("Issued at: {}", claims.iat);
69            println!("Expires at: {}", claims.exp);
70        }
71        Err(e) => {
72            println!("Token validation failed: {e}");
73        }
74    }
75
76    println!("\nExample completed! Check the documentation for real implementation details.");
77    Ok(())
78}
Source

pub fn validate_session(&self, token: &str) -> Result<Claims>

Validate a JWT session token

Verifies that a JWT token is:

  • Properly formatted
  • Signed with the correct secret
  • Not expired
  • Contains valid claims
§Arguments
  • token - JWT token string to validate
§Returns
  • Ok(Claims) - Token is valid, returns parsed claims
  • Err(AuthError) - Token is invalid, expired, or malformed
§Example
use ecdsa_jwt::{auth::{AuthService, AuthRequest},config::JwtConfig};
use secrecy::Secret;
use base64::prelude::*;

let config = JwtConfig {  
    secret: Secret::new(BASE64_STANDARD.encode("your-secret")),
    ttl: 3600, // 1 hour
};

let auth_service = AuthService::new(config);
let jwt_token = "token";
match auth_service.validate_session(&jwt_token) {
    Ok(claims) => {
        println!("Valid session for user: {}", claims.sub);
        // Allow access to protected resource
    }
    Err(_) => {
        println!("Invalid token - access denied");
        // Return 401 Unauthorized
    }
}
Examples found in repository?
examples/basic_workflow.rs (line 64)
11fn main() -> Result<(), Box<dyn std::error::Error>> {
12    println!("ECDSA-JWT Basic Usage Example");
13
14    // 1. Setup authentication service
15    let jwt_config = JwtConfig {
16        secret: Secret::new(BASE64_STANDARD.encode("example-secret-key")),
17        ttl: 3600, // 1 hour
18    };
19    let auth_service = AuthService::new(jwt_config);
20
21    // 2. Simulate challenge storage (in real app, use Redis/DB)
22    let mut challenges: HashMap<String, String> = HashMap::new();
23
24    // 3. Generate challenge
25    let challenge = auth_service.generate_challenge();
26    let session_id = "example-session-123";
27    challenges.insert(session_id.to_string(), challenge.clone());
28
29    println!("Generated challenge: {challenge}");
30    println!("Stored with session ID: {session_id}");
31
32    // 4. Simulate client authentication (this would normally fail without real signature)
33    println!("\n Authentication attempt...");
34
35    let auth_request = AuthRequest {
36        challenge,
37        signature: "dummy-signature-for-example".to_string(),
38        public_key: "-----BEGIN PUBLIC KEY-----\nDummyKeyForExample\n-----END PUBLIC KEY-----"
39            .to_string(),
40    };
41
42    // This will fail, but demonstrates the API structure
43    match auth_service.authenticate(auth_request, false) {
44        Ok(response) => {
45            println!("Authentication successful!");
46            println!("   JWT Token: {}", response.session_token);
47            println!("   Session ID: {}", response.session_id);
48            println!("   Expires at: {}", response.expires_at);
49        }
50        Err(e) => {
51            println!(" Authentication failed (expected with dummy data): {e}");
52            println!("   In real usage, provide valid signature and public key.");
53        }
54    }
55
56    // 5. Demonstrate JWT validation with a real token
57    println!("\nJWT Token Operations...");
58
59    let session_id = uuid::Uuid::new_v4();
60    let jwt_token = ecdsa_jwt::crypto::jwt::create_jwt(session_id, None, &auth_service.jwt_config)?;
61
62    println!("Created JWT: {}...", &jwt_token[..50]);
63
64    match auth_service.validate_session(&jwt_token) {
65        Ok(claims) => {
66            println!("Token validation successful!");
67            println!("User/Session ID: {}", claims.sub);
68            println!("Issued at: {}", claims.iat);
69            println!("Expires at: {}", claims.exp);
70        }
71        Err(e) => {
72            println!("Token validation failed: {e}");
73        }
74    }
75
76    println!("\nExample completed! Check the documentation for real implementation details.");
77    Ok(())
78}
Source

pub fn verify_signature_from_jwt( &self, jwt_token: &str, challenge: &[u8], signature: &[u8], ) -> Result<()>

Verify a signature using the public key from JWT claims

This method extracts the public key from the JWT and uses it to verify a signature, eliminating the need to pass the public key separately.

§Arguments
  • jwt_token - JWT token containing public key information
  • challenge - Challenge bytes that were signed
  • signature - Signature bytes to verify
§Returns
  • Ok(()) if signature is valid
  • Err(AuthError) if verification fails
§Example
use ecdsa_jwt::auth::AuthService;
use ecdsa_jwt::config::JwtConfig;
use secrecy::Secret;
use base64::prelude::*;

let jwt_config = JwtConfig {
    secret: Secret::new(BASE64_STANDARD.encode("test-secret")),
    ttl: 3600,
};
let auth_service = AuthService::new(jwt_config);

// This would normally be a real JWT token with embedded public key
let jwt_token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...";
let challenge = b"challenge bytes";
let signature = &[0x30, 0x44, 0x02, 0x20]; // Example DER signature

// Note: This will fail with a real JWT that doesn't contain a public key
// This is just demonstrating the API usage
let _result = auth_service.verify_signature_from_jwt(jwt_token, challenge, signature);

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> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

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> Same for T

Source§

type Output = T

Should always be Self
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