//// atproto session auth: exchange an identifier + password for session tokens //// via `com.atproto.server.createSession`, and refresh them via //// `com.atproto.server.refreshSession`. import atproto/xrpc.{type Client, type XrpcError} import gleam/dynamic/decode import gleam/json import gleam/option.{None, Some} import gleam/result pub type SessionTokens { SessionTokens( did: String, handle: String, access_jwt: String, refresh_jwt: String, ) } pub fn create_session( client: Client, pds: String, identifier: String, password: String, ) -> Result(SessionTokens, XrpcError) { let body = json.object([ #("identifier", json.string(identifier)), #("password", json.string(password)), ]) use resp <- result.try(xrpc.post_json( client, pds <> "/xrpc/com.atproto.server.createSession", None, body, )) xrpc.parse(resp.body, tokens_decoder()) } /// Exchange a refresh JWT for a fresh session (the refresh token is sent as the /// bearer credential, per `com.atproto.server.refreshSession`). pub fn refresh_session( client: Client, pds: String, refresh_jwt: String, ) -> Result(SessionTokens, XrpcError) { use resp <- result.try(xrpc.post_json( client, pds <> "/xrpc/com.atproto.server.refreshSession", Some(refresh_jwt), json.object([]), )) xrpc.parse(resp.body, tokens_decoder()) } fn tokens_decoder() -> decode.Decoder(SessionTokens) { use did <- decode.field("did", decode.string) use handle <- decode.field("handle", decode.string) use access_jwt <- decode.field("accessJwt", decode.string) use refresh_jwt <- decode.field("refreshJwt", decode.string) decode.success(SessionTokens(did:, handle:, access_jwt:, refresh_jwt:)) }