1use std::fmt;
133use std::pin::Pin;
134use std::time::{Duration, SystemTime, SystemTimeError, UNIX_EPOCH};
135
136use async_trait::async_trait;
137use base64::prelude::*;
138use ed25519_dalek::{SignatureError, Signer, Verifier};
139use futures::Future;
140use serde::de::DeserializeOwned;
141use serde::{Deserialize, Serialize};
142
143pub use ed25519_dalek::{Signature, SigningKey, VerifyingKey};
144pub use rand::rngs::OsRng;
145
146#[derive(Copy, Clone, Debug, Eq, PartialEq)]
148pub enum ErrorKind {
149 Auth,
151 Base64,
152 Fetch,
153 Format,
154 Json,
155 Time,
156}
157
158#[derive(Debug)]
160pub struct Error {
161 kind: ErrorKind,
162 message: String,
163}
164
165impl Error {
166 pub fn new(kind: ErrorKind, message: String) -> Self {
168 Self { kind, message }
169 }
170
171 pub fn kind(&self) -> ErrorKind {
173 self.kind
174 }
175
176 pub fn into_inner(self) -> (ErrorKind, String) {
178 (self.kind, self.message)
179 }
180
181 pub fn auth<M: fmt::Display>(message: M) -> Self {
183 Self::new(ErrorKind::Auth, message.to_string())
184 }
185
186 pub fn format<M: fmt::Display>(cause: M) -> Self {
188 Self::new(ErrorKind::Format, cause.to_string())
189 }
190
191 pub fn fetch<Info: fmt::Debug>(info: Info) -> Self {
193 Self::new(ErrorKind::Fetch, format!("{info:?}"))
194 }
195}
196
197impl fmt::Display for Error {
198 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
199 write!(f, "{:?}: {}", self.kind, self.message)
200 }
201}
202
203impl std::error::Error for Error {}
204
205impl From<base64::DecodeError> for Error {
206 fn from(cause: base64::DecodeError) -> Self {
207 Self::new(ErrorKind::Base64, cause.to_string())
208 }
209}
210
211impl From<serde_json::Error> for Error {
212 fn from(cause: serde_json::Error) -> Self {
213 Self::new(ErrorKind::Json, cause.to_string())
214 }
215}
216
217impl From<SignatureError> for Error {
218 fn from(cause: SignatureError) -> Self {
219 Self::new(ErrorKind::Auth, cause.to_string())
220 }
221}
222
223impl From<SystemTimeError> for Error {
224 fn from(cause: SystemTimeError) -> Self {
225 Self::new(ErrorKind::Time, cause.to_string())
226 }
227}
228
229#[async_trait]
231pub trait Resolve: Send + Sync {
232 type HostId: Serialize + DeserializeOwned + fmt::Debug + Send + Sync;
233 type ActorId: Serialize + DeserializeOwned + fmt::Debug + Send + Sync;
234 type Claims: Serialize + DeserializeOwned + Send + Sync;
235
236 async fn resolve(
238 &self,
239 host: &Self::HostId,
240 actor_id: &Self::ActorId,
241 ) -> Result<Actor<Self::ActorId>, Error>;
242
243 async fn verify(
245 &self,
246 encoded: String,
247 now: SystemTime,
248 ) -> Result<SignedToken<Self::HostId, Self::ActorId, Self::Claims>, Error>
249 where
250 Self::ActorId: PartialEq,
251 {
252 let claims = verify_claims(self, &encoded, now).await?;
253 Ok(SignedToken::new(claims, encoded))
254 }
255}
256
257async fn decode_and_verify_token<R: Resolve + ?Sized>(
258 resolver: &R,
259 encoded: &str,
260 now: SystemTime,
261) -> Result<Token<R::HostId, R::ActorId, R::Claims>, Error>
262where
263 R::ActorId: PartialEq,
264{
265 let (message, signature) = token_signature(encoded)?;
266 let token: Token<R::HostId, R::ActorId, R::Claims> = decode_token(message)?;
267
268 if token.is_expired(now) {
269 return Err(Error::new(ErrorKind::Time, "token is expired".into()));
270 }
271
272 let actor = resolver.resolve(&token.iss, &token.actor_id).await?;
273
274 if actor.id != token.actor_id {
275 return Err(Error::auth(
276 "attempted to use a bearer token for a different actor",
277 ));
278 }
279
280 if let Err(cause) = actor.public_key().verify(message.as_bytes(), &signature) {
281 Err(Error::auth(format!("invalid bearer token: {cause}")))
282 } else {
283 Ok(token)
284 }
285}
286
287type Verification<'a, H, A, C> =
288 Pin<Box<dyn Future<Output = Result<Claims<H, A, C>, Error>> + Send + 'a>>;
289
290fn verify_claims<'a, R>(
291 resolver: &'a R,
292 encoded: &'a str,
293 now: SystemTime,
294) -> Verification<'a, R::HostId, R::ActorId, R::Claims>
295where
296 R: Resolve + ?Sized,
297 R::ActorId: PartialEq,
298{
299 Box::pin(async move {
300 let token = decode_and_verify_token(resolver, encoded, now).await?;
301
302 if let Some(parent) = token.inherit {
303 let parent_claims = verify_claims(resolver, &parent, now).await?;
304
305 if token.exp <= parent_claims.exp {
306 parent_claims.consume(token.iss, token.actor_id, token.custom)
307 } else {
308 Err(Error::new(
309 ErrorKind::Time,
310 "cannot extend the expiration time of a recursive token".into(),
311 ))
312 }
313 } else {
314 Ok(Claims::new(
315 token.exp,
316 token.iss,
317 token.actor_id,
318 token.custom,
319 ))
320 }
321 })
322}
323
324enum Key {
325 Public(VerifyingKey),
326 Private(SigningKey),
327}
328
329impl Key {
330 fn has_private_key(&self) -> bool {
331 match &self {
332 Self::Public(_) => false,
333 Self::Private(_) => true,
334 }
335 }
336}
337
338pub struct Actor<A> {
348 id: A,
349 key: Key,
350}
351
352impl<A> Actor<A> {
353 pub fn new(id: A) -> Self {
355 Self::with_keypair(id, SigningKey::generate(&mut OsRng))
356 }
357
358 pub fn with_keypair(id: A, keypair: SigningKey) -> Self {
360 Self {
361 id,
362 key: Key::Private(keypair),
363 }
364 }
365
366 pub fn with_public_key(id: A, public_key: VerifyingKey) -> Self {
368 Self {
369 id,
370 key: Key::Public(public_key),
371 }
372 }
373
374 pub fn id(&self) -> &A {
376 &self.id
377 }
378
379 pub fn has_private_key(&self) -> bool {
381 self.key.has_private_key()
382 }
383
384 pub fn public_key(&self) -> VerifyingKey {
386 match &self.key {
387 Key::Public(public_key) => *public_key,
388 Key::Private(keypair) => keypair.verifying_key(),
389 }
390 }
391
392 fn sign_token_inner<H, C>(&self, token: &Token<H, A, C>) -> Result<String, Error>
393 where
394 H: Serialize,
395 A: Serialize,
396 C: Serialize,
397 {
398 let keypair = match &self.key {
399 Key::Private(keypair) => Ok(keypair),
400 Key::Public(_) => Err(Error::auth("cannot sign a token without a private key")),
401 }?;
402
403 let header = BASE64_STANDARD.encode(serde_json::to_string(&TokenHeader::default())?);
404 let claims = BASE64_STANDARD.encode(serde_json::to_string(&token)?);
405
406 let signature = keypair.try_sign(format!("{header}.{claims}").as_bytes())?;
407 let signature = BASE64_STANDARD.encode(signature.to_bytes());
408
409 Ok(format!("{header}.{claims}.{signature}"))
410 }
411
412 pub fn sign_token<H, C>(&self, token: Token<H, A, C>) -> Result<SignedToken<H, A, C>, Error>
414 where
415 H: Serialize,
416 A: Serialize,
417 C: Serialize,
418 {
419 let jwt = self.sign_token_inner(&token)?;
420
421 let claims = Claims {
422 exp: token.exp,
423 host: token.iss,
424 actor_id: token.actor_id,
425 claims: token.custom,
426 inherit: None,
427 };
428
429 Ok(SignedToken::new(claims, jwt))
430 }
431
432 pub fn consume_and_sign<H, C>(
434 &self,
435 token: SignedToken<H, A, C>,
436 host_id: H,
437 claims: C,
438 now: SystemTime,
439 ) -> Result<SignedToken<H, A, C>, Error>
440 where
441 H: Serialize + Clone,
442 A: Serialize + Clone,
443 C: Serialize + Clone,
444 {
445 let (token, claims) = Token::consume(token, now, host_id.clone(), self.id.clone(), claims)?;
446 let token = self.sign_token_inner(&token)?;
447 Ok(SignedToken::new(claims, token))
448 }
449}
450
451impl<A: Clone> Clone for Actor<A> {
452 fn clone(&self) -> Self {
453 Actor {
454 id: self.id.clone(),
455 key: match &self.key {
456 Key::Public(public_key) => Key::Public(*public_key),
457 Key::Private(keypair) => Key::Public(keypair.verifying_key()),
458 },
459 }
460 }
461}
462
463impl<A: fmt::Debug> fmt::Debug for Actor<A> {
464 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
465 write!(f, "actor {:?}", self.id)
466 }
467}
468
469#[derive(Eq, PartialEq, Debug, Deserialize, Serialize)]
470struct TokenHeader {
471 alg: String,
472 typ: String,
473}
474
475impl Default for TokenHeader {
476 fn default() -> TokenHeader {
477 TokenHeader {
478 alg: "ES256".into(),
479 typ: "JWT".into(),
480 }
481 }
482}
483
484#[derive(Clone, Debug, Eq, PartialEq)]
486pub struct Claims<H, A, C> {
487 exp: u64,
488 host: H,
489 actor_id: A,
490 claims: C,
491 inherit: Option<Box<Claims<H, A, C>>>,
492}
493
494impl<H, A, C> Claims<H, A, C> {
495 fn new(exp: u64, host: H, actor_id: A, claims: C) -> Self {
496 Self {
497 exp,
498 host,
499 actor_id,
500 claims,
501 inherit: None,
502 }
503 }
504
505 fn consume(self, host: H, actor_id: A, claims: C) -> Result<Self, Error> {
506 let exp = self.expires().duration_since(UNIX_EPOCH)?;
507
508 Ok(Self {
509 exp: exp.as_secs(),
510 host,
511 actor_id,
512 claims,
513 inherit: Some(Box::new(self)),
514 })
515 }
516
517 fn expires(&self) -> SystemTime {
518 UNIX_EPOCH + Duration::from_secs(self.exp)
519 }
520}
521
522pub struct Iter<'a, H, A, C> {
523 claims: Option<&'a Claims<H, A, C>>,
524}
525
526impl<'a, H: 'a, A: 'a, C: 'a> Iterator for Iter<'a, H, A, C> {
527 type Item = (&'a H, &'a A, &'a C);
528
529 fn next(&mut self) -> Option<Self::Item> {
530 let claims = self.claims?;
531 let item = (&claims.host, &claims.actor_id, &claims.claims);
532 self.claims = claims.inherit.as_ref().map(|claims| &**claims);
533 Some(item)
534 }
535}
536
537impl<H, A, C> Claims<H, A, C> {
538 pub fn iter(&self) -> Iter<H, A, C> {
539 Iter { claims: Some(self) }
540 }
541}
542
543impl<H: PartialEq, A: PartialEq, C> Claims<H, A, C> {
544 pub fn get(&self, host: &H, actor_id: &A) -> Option<&C> {
546 self.iter()
547 .filter_map(|(h, a, c)| {
548 if h == host && a == actor_id {
549 Some(c)
550 } else {
551 None
552 }
553 })
554 .next()
555 }
556}
557
558impl<'a, H, A, C> IntoIterator for &'a Claims<H, A, C> {
559 type Item = (&'a H, &'a A, &'a C);
560 type IntoIter = Iter<'a, H, A, C>;
561
562 fn into_iter(self) -> Self::IntoIter {
563 self.iter()
564 }
565}
566
567#[derive(Clone, Eq, PartialEq, Deserialize, Serialize)]
569pub struct Token<H, A, C> {
570 iss: H,
571 iat: u64,
572 exp: u64,
573 actor_id: A,
574 custom: C,
575 inherit: Option<String>,
576}
577
578impl<H, A, C> Token<H, A, C> {
579 pub fn new(iss: H, iat: SystemTime, ttl: Duration, actor_id: A, claims: C) -> Self {
581 let iat = iat.duration_since(UNIX_EPOCH).expect("duration");
582 let exp = iat + ttl;
583
584 Self {
585 iss,
586 iat: iat.as_secs(),
587 exp: exp.as_secs(),
588 actor_id,
589 custom: claims,
590 inherit: None,
591 }
592 }
593
594 fn consume(
595 parent: SignedToken<H, A, C>,
596 iat: SystemTime,
597 host_id: H,
598 actor_id: A,
599 claims: C,
600 ) -> Result<(Self, Claims<H, A, C>), Error>
601 where
602 H: Clone,
603 A: Clone,
604 C: Clone,
605 {
606 let iat = iat.duration_since(UNIX_EPOCH)?;
607 let exp = parent.expires().duration_since(UNIX_EPOCH)?;
608
609 let token = Self {
610 iss: host_id.clone(),
611 iat: iat.as_secs(),
612 exp: exp.as_secs(),
613 actor_id: actor_id.clone(),
614 custom: claims.clone(),
615 inherit: Some(parent.jwt),
616 };
617
618 let claims = parent.claims.consume(host_id, actor_id, claims)?;
619
620 Ok((token, claims))
621 }
622
623 pub fn issuer(&self) -> &H {
625 &self.iss
626 }
627
628 pub fn actor_id(&self) -> &A {
630 &self.actor_id
631 }
632
633 pub fn is_expired(&self, now: SystemTime) -> bool {
635 let iat = UNIX_EPOCH + Duration::from_secs(self.iat);
636 let exp = UNIX_EPOCH + Duration::from_secs(self.exp);
637 now < iat || now >= exp
638 }
639}
640
641impl<H: fmt::Display, A: fmt::Display, C> fmt::Debug for Token<H, A, C> {
642 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
643 write!(
644 f,
645 "JWT token claiming to authenticate actor {} at host {}",
646 self.actor_id, self.iss
647 )
648 }
649}
650
651#[derive(Clone, Eq, PartialEq)]
653pub struct SignedToken<H, A, C> {
654 claims: Claims<H, A, C>,
655 jwt: String,
656}
657
658impl<H, A, C> SignedToken<H, A, C> {
659 fn new(data: Claims<H, A, C>, jwt: String) -> Self {
660 Self { claims: data, jwt }
661 }
662
663 pub fn claims(&self) -> &Claims<H, A, C> {
665 &self.claims
666 }
667
668 pub fn expires(&self) -> SystemTime {
670 self.claims.expires()
671 }
672
673 pub fn jwt(&self) -> &str {
675 &self.jwt
676 }
677
678 pub fn into_jwt(self) -> String {
680 self.jwt
681 }
682}
683
684impl<H: fmt::Debug, A: fmt::Debug, C: fmt::Debug> fmt::Debug for SignedToken<H, A, C> {
685 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
686 write!(f, "JWT {} which claims {:?}", self.jwt, self.claims)
687 }
688}
689
690fn token_signature(encoded: &str) -> Result<(&str, Signature), Error> {
691 if encoded.ends_with('.') {
692 return Err(Error::format("encoded token cannot end with ."));
693 }
694
695 let i = encoded
696 .rfind('.')
697 .ok_or_else(|| Error::format(format!("invalid token: {}", encoded)))?;
698
699 let message = &encoded[..i];
700
701 let signature = BASE64_STANDARD
702 .decode(&encoded[(i + 1)..])
703 .map_err(|e| Error::new(ErrorKind::Base64, e.to_string()))?;
704
705 let signature = Signature::try_from(&signature[..])?;
706
707 Ok((message, signature))
708}
709
710fn decode_token<H, A, C>(encoded: &str) -> Result<Token<H, A, C>, Error>
711where
712 H: DeserializeOwned,
713 A: DeserializeOwned,
714 C: DeserializeOwned,
715{
716 let i = encoded
717 .find('.')
718 .ok_or_else(|| Error::format(format!("invalid token: {}", encoded)))?;
719
720 let header = BASE64_STANDARD.decode(&encoded[..i])?;
721 let header: TokenHeader = serde_json::from_slice(&header)?;
722
723 if header != TokenHeader::default() {
724 return Err(Error::format(format!(
725 "unsupported bearer token type: {header:?}"
726 )));
727 }
728
729 let token = BASE64_STANDARD.decode(&encoded[(i + 1)..])?;
730 let token = serde_json::from_slice(&token)?;
731
732 Ok(token)
733}
734
735#[cfg(test)]
736mod tests {
737 use super::*;
738
739 const SIZE_LIMIT: usize = 8000; #[test]
742 fn test_format() {
743 let actor = Actor::new("actor".to_string());
744 let token = Token::new(
745 "example.com".to_string(),
746 SystemTime::now(),
747 Duration::from_secs(30),
748 actor.id().to_string(),
749 (),
750 );
751
752 let signed = actor.sign_token(token).unwrap();
753 let (message, _) = token_signature(signed.jwt()).unwrap();
754
755 assert!(signed.jwt().starts_with(message));
756 assert!(signed.jwt().len() < SIZE_LIMIT);
757 }
758}