module Dpop exposing ( PendingAction(..) , SignParams , actionFromId , actionId , dpopDecoder , sessionEncoder , sha256Decoder , signFor , xrpcSignInput ) import Json.Decode as Decode import Json.Encode as Encode import Ports import Types exposing (Session) type PendingAction = ActionPAR | ActionToken | ActionRefresh | ActionListPublications | ActionListDocuments | ActionCreatePublication | ActionPutPublication String | ActionUploadIcon type alias SignParams = { htm : String , htu : String , nonce : Maybe String , accessToken : Maybe String } actionId : PendingAction -> String actionId action = case action of ActionPAR -> "par" ActionToken -> "token" ActionRefresh -> "refresh" ActionListPublications -> "listPubs" ActionListDocuments -> "listDocs" ActionCreatePublication -> "createPub" ActionPutPublication rkey -> "putPub:" ++ rkey ActionUploadIcon -> "uploadIcon" actionFromId : String -> Maybe PendingAction actionFromId id = if id == "par" then Just ActionPAR else if id == "token" then Just ActionToken else if id == "refresh" then Just ActionRefresh else if id == "listPubs" then Just ActionListPublications else if id == "listDocs" then Just ActionListDocuments else if id == "createPub" then Just ActionCreatePublication else if id == "uploadIcon" then Just ActionUploadIcon else if String.startsWith "putPub:" id then Just (ActionPutPublication (String.dropLeft 7 id)) else Nothing signFor : PendingAction -> SignParams -> Cmd msg signFor action params = let fields : List ( String, Encode.Value ) fields = [ ( "id", Encode.string (actionId action) ) , ( "htm", Encode.string params.htm ) , ( "htu", Encode.string params.htu ) ] ++ (params.nonce |> Maybe.map (\n -> [ ( "nonce", Encode.string n ) ]) |> Maybe.withDefault []) ++ (params.accessToken |> Maybe.map (\t -> [ ( "accessToken", Encode.string t ) ]) |> Maybe.withDefault []) in Ports.signDpop (Encode.object fields) xrpcSignInput : Session -> String -> SignParams xrpcSignInput session method = { htm = if method == "listRecords" then "GET" else "POST" , htu = session.pdsEndpoint ++ "/xrpc/com.atproto.repo." ++ method , nonce = Nothing , accessToken = Just session.accessToken } dpopDecoder : Decode.Decoder (Result String { id : String, jwt : String }) dpopDecoder = Decode.field "id" Decode.string |> Decode.andThen (\id -> Decode.field "error" (Decode.nullable Decode.string) |> Decode.andThen (\maybeErr -> case maybeErr of Just e -> Decode.succeed (Err e) Nothing -> Decode.field "jwt" Decode.string |> Decode.map (\jwt -> Ok { id = id, jwt = jwt }) ) ) sha256Decoder : Decode.Decoder { id : String, v : String } sha256Decoder = Decode.map2 (\id v -> { id = id, v = v }) (Decode.field "id" Decode.string) (Decode.field "value" Decode.string) sessionEncoder : Session -> Encode.Value sessionEncoder s = Encode.object [ ( "did", Encode.string s.did ) , ( "handle", Encode.string s.handle ) , ( "pdsEndpoint", Encode.string s.pdsEndpoint ) , ( "tokenEndpoint", Encode.string s.tokenEndpoint ) , ( "accessToken", Encode.string s.accessToken ) , ( "refreshToken", s.refreshToken |> Maybe.map Encode.string |> Maybe.withDefault Encode.null ) ]