|
4 | 4 | // SPDX-License-Identifier: AGPL-3.0-only |
5 | 5 | // Please see LICENSE in the repository root for full details. |
6 | 6 |
|
| 7 | +use base64ct::{Base64Url, Encoding}; |
7 | 8 | use chrono::{DateTime, Utc}; |
8 | 9 | use crc::{Crc, CRC_32_ISO_HDLC}; |
9 | 10 | use mas_iana::oauth::OAuthTokenTypeHint; |
@@ -294,7 +295,7 @@ impl TokenType { |
294 | 295 | pub fn check(token: &str) -> Result<TokenType, TokenFormatError> { |
295 | 296 | // these are legacy tokens imported from Synapse |
296 | 297 | // we don't do any validation on them and continue as is |
297 | | - if token.starts_with("syt_") { |
| 298 | + if token.starts_with("syt_") || is_likely_synapse_macaroon(token) { |
298 | 299 | return Ok(TokenType::CompatAccessToken); |
299 | 300 | } |
300 | 301 | if token.starts_with("syr_") { |
@@ -344,6 +345,20 @@ impl PartialEq<OAuthTokenTypeHint> for TokenType { |
344 | 345 | } |
345 | 346 | } |
346 | 347 |
|
| 348 | +/// Returns true if and only if a token looks like it may be a macaroon. |
| 349 | +/// |
| 350 | +/// Macaroons are a standard for tokens that support attenuation. |
| 351 | +/// Synapse used them for old sessions and for guest sessions. |
| 352 | +/// |
| 353 | +/// We won't bother to decode them fully, but we can check to see if the first |
| 354 | +/// constraint is the `location` constraint. |
| 355 | +fn is_likely_synapse_macaroon(token: &str) -> bool { |
| 356 | + let Ok(decoded) = Base64Url::decode_vec(token) else { |
| 357 | + return false; |
| 358 | + }; |
| 359 | + decoded.get(4..13) == Some(b"location ") |
| 360 | +} |
| 361 | + |
347 | 362 | const NUM: [u8; 62] = *b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; |
348 | 363 |
|
349 | 364 | fn base62_encode(mut num: u32) -> String { |
@@ -420,6 +435,25 @@ mod tests { |
420 | 435 | ); |
421 | 436 | } |
422 | 437 |
|
| 438 | + #[test] |
| 439 | + fn test_is_likely_synapse_macaroon() { |
| 440 | + // This is just the prefix of a Synapse macaroon, but it's enough to make the |
| 441 | + // sniffing work |
| 442 | + assert!(is_likely_synapse_macaroon( |
| 443 | + "MDAxYmxvY2F0aW9uIGxpYnJlcHVzaC5uZXQKMDAx" |
| 444 | + )); |
| 445 | + |
| 446 | + // Whilst this is a macaroon, it's not a Synapse macaroon |
| 447 | + assert!(! is_likely_synapse_macaroon("MDAxY2xvY2F0aW9uIGh0dHA6Ly9teWJhbmsvCjAwMjZpZGVudGlmaWVyIHdlIHVzZWQgb3VyIHNlY3JldCBrZXkKMDAyZnNpZ25hdHVyZSDj2eApCFJsTAA5rhURQRXZf91ovyujebNCqvD2F9BVLwo")); |
| 448 | + |
| 449 | + // None of these are macaroons |
| 450 | + assert!(!is_likely_synapse_macaroon( |
| 451 | + "eyJARTOhearotnaeisahtoarsnhiasra.arsohenaor.oarnsteao" |
| 452 | + )); |
| 453 | + assert!(!is_likely_synapse_macaroon("....")); |
| 454 | + assert!(!is_likely_synapse_macaroon("aaa")); |
| 455 | + } |
| 456 | + |
423 | 457 | #[test] |
424 | 458 | fn test_generate_and_check() { |
425 | 459 | const COUNT: usize = 500; // Generate 500 of each token type |
|
0 commit comments