~ knotless Search/
↑↓ move↵ open⇧↵ all results

grain.social/grain

photography-focused photo sharing platform

grain.social
1 collaborator
0
2

The import carries the web's OAuth client key

Merged chadtmiller.com asked to merge into feature/rust from grain.social/grain:web/hatk-sessions

Only what this pull adds. It is built on grain runs on a Rust server modelled on knotless, without hatk.

crates/grain-import/src/lib.rs+15−1
@@ -56,7 +56,13 @@
//! `_cursor`'s Jetstream rows (`jetstream:<url>`) are `stream_cursor`'s,
//! so the stream resumes where hatk left it.
//!
//! Not imported: hatk's OAuth tables (web sessions sign in again), its
//! Of `_oauth_keys`, `pds-client-auth-key` is `oauth_key`'s, the private
//! JWK as hatk kept it: grain's OAuth client goes on signing with the key
//! its JWKS has always named, so each PDS goes on knowing the client.
//!
//! Not imported: hatk's other OAuth keys and its sessions, web and app
//! alike (every account logs in again: nothing grain-serve runs reads the
//! cookies that named them), its
//! search tables, classifier scores, space sync state, and records of
//! collections the index does not keep. The summary names each such table
//! and its rows.
@@ -398,6 +404,14 @@ pub const KEPT: &[Kept] = &[
insert: "INSERT INTO space_invite (space, member_did, author_did, created_at)
VALUES (?1, ?2, ?3, ?4)",
},
Kept {
table: "oauth_key",
source: "_oauth_keys",
from: "_oauth_keys",
count: "SELECT count(*) FROM _oauth_keys WHERE kid = 'pds-client-auth-key'",
select: "SELECT kid, private_key FROM _oauth_keys WHERE kid = 'pds-client-auth-key'",
insert: "INSERT INTO oauth_key (kid, jwk) VALUES (?1, ?2)",
},
Kept {
table: "space_support",
source: "_space_support",
crates/grain-import/src/tests.rs+24−2
@@ -142,7 +142,8 @@ INSERT INTO _space_invites VALUES
INSERT INTO _space_support VALUES
('https://pds.example', 1, '[]', '2025-01-01T00:00:00.000Z'),
('https://old.example', 0, '["com.atproto.space.createSpace"]', '2025-01-02T00:00:00.000Z');
INSERT INTO _oauth_keys VALUES ('k1', 'private', 'public', '2025-01-01 00:00:00');
INSERT INTO _oauth_keys VALUES ('appview-oauth-key', 'server', 'public', '2025-01-01 00:00:00'),
('pds-client-auth-key', '{"key_ops":["sign"],"ext":true,"kty":"EC","x":"6wVOcgpdJpSGVwhFhUNcCtEGehv3oVbm3Vfspb8z_7w","y":"ezlUp0nPpJHIlRVbUBOb0KitkEVV7uLvtdfWFUmHgzw","crv":"P-256","d":"7YBtWZgfb9lUSihC1h2-HySNJopUjlS4GBsoVC-rZd4"}', 'public', '2025-01-01 00:00:00');
INSERT INTO _oauth_sessions VALUES ('did:plc:alice', 'https://pds.example', 'a', 'r', 'jkt');
INSERT INTO _fts_social_grain_gallery VALUES ('at://did:plc:alice/social.grain.gallery/3l3qo2vuowo2a', 'Fog');
"#;
@@ -269,6 +270,10 @@ fn hatk(dir: &Path, sql: &str) -> PathBuf {
path
}
/// A client key as WebCrypto exports one, with the members
/// it adds.
const CLIENT_KEY: &str = r#"{"key_ops":["sign"],"ext":true,"kty":"EC","x":"6wVOcgpdJpSGVwhFhUNcCtEGehv3oVbm3Vfspb8z_7w","y":"ezlUp0nPpJHIlRVbUBOb0KitkEVV7uLvtdfWFUmHgzw","crv":"P-256","d":"7YBtWZgfb9lUSihC1h2-HySNJopUjlS4GBsoVC-rZd4"}"#;
/// The whole of hatk's fixture database in `dir`.
fn full(dir: &Path) -> PathBuf {
hatk(dir, &format!("{KEPT_ROWS}{}", record_rows()))
@@ -443,15 +448,33 @@ fn every_row_of_what_the_network_cannot_rebuild_is_imported() {
("push_token", 3, 3),
("story_view", 2, 2),
("space_invite", 2, 2),
("oauth_key", 1, 1),
("space_support", 2, 2),
]
);
assert!(summary.failed.is_empty(), "{:?}", summary.failed);
// The client's key, and none of the others.
assert_eq!(
rows("SELECT kid, jwk FROM oauth_key"),
[format!("pds-client-auth-key|{CLIENT_KEY}")]
);
assert_eq!(summary.unrecognized, [("weird".to_string(), 1)]);
// As the index reads them.
drop(db);
let index = Index::open(&into).unwrap();
// The web's client signs with it, as it was exported.
let web =
grain_serve::login::Web::new("https://grain.social", &index, "https://pds.test").unwrap();
let jwks = web.client.jwks();
let key = &jwks["keys"][0];
assert_eq!(
(&key["kid"], &key["x"]),
(
&json!("pds-client-auth-key"),
&json!("6wVOcgpdJpSGVwhFhUNcCtEGehv3oVbm3Vfspb8z_7w")
)
);
assert_eq!(index.cursor(JETSTREAM).unwrap(), Some(1730000000000000));
assert!(index.taken_down("did:plc:bob").unwrap());
assert!(!index.taken_down("did:plc:carol").unwrap());
@@ -609,7 +632,6 @@ fn every_record_is_imported_as_it_was_indexed() {
// What is not imported, named with its rows.
let left: HashMap<_, _> = summary.left.iter().cloned().collect();
let expected = [
("_oauth_keys", 1),
("_oauth_sessions", 1),
("app.bsky.actor.profile", 1),
("app.bsky.feed.post__entities", 0),