|
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