Try all secrets in list for decoding signed cookies.

This commit is contained in:
Dillon Kearns 2022-01-20 19:09:37 -08:00
parent b7185adc39
commit 08e635cbeb
2 changed files with 59 additions and 95 deletions

View File

@ -57,41 +57,23 @@ withSession config decoder toRequest =
Request.cookie config.name
|> Request.map
(\maybeSessionCookie ->
--DataSource.succeed
-- (case maybeSessionCookie of
-- Just sessionCookie ->
-- Debug.todo ""
--
-- Nothing ->
-- Debug.todo ""
-- )
let
result : Result String decoded
result =
--Err ""
case maybeSessionCookie of
Nothing ->
Err "TODO"
Just sessionCookie ->
OptimizedDecoder.decodeString decoder sessionCookie
|> Result.mapError Json.Decode.errorToString
--decrypted : DataSource (Dict String Json.Decode.Value)
decrypted : DataSource (Result String decoded)
decrypted =
case maybeSessionCookie of
Just sessionCookie ->
decrypt decoder sessionCookie
decrypt config.secrets decoder sessionCookie
|> DataSource.map Ok
Nothing ->
Err "TODO"
|> DataSource.succeed
decryptedFull : DataSource (Dict String OptimizedDecoder.Value)
decryptedFull =
maybeSessionCookie
|> Maybe.map
(\sessionCookie -> decrypt (OptimizedDecoder.dict OptimizedDecoder.value) sessionCookie)
(\sessionCookie -> decrypt config.secrets (OptimizedDecoder.dict OptimizedDecoder.value) sessionCookie)
|> Maybe.withDefault (DataSource.succeed Dict.empty)
in
decryptedFull
@ -100,6 +82,7 @@ withSession config decoder toRequest =
DataSource.andThen
(\( sessionUpdate, response ) ->
let
encodedCookie : Json.Encode.Value
encodedCookie =
Session.setValues sessionUpdate cookieDict
in
@ -126,11 +109,6 @@ withSession config decoder toRequest =
encrypt : Secrets.Value (List String) -> Json.Encode.Value -> DataSource.DataSource String
encrypt secrets input =
let
decoder : OptimizedDecoder.Decoder String
decoder =
OptimizedDecoder.string
in
DataSource.Http.request
(secrets
|> Secrets.map
@ -140,31 +118,44 @@ encrypt secrets input =
, headers = []
-- TODO pass through secrets here
, body = DataSource.Http.jsonBody input
, body =
DataSource.Http.jsonBody
(Json.Encode.object
[ ( "values", input )
, ( "secret"
, Json.Encode.string
(secretList
|> List.head
-- TODO use different default - require non-empty list?
|> Maybe.withDefault ""
)
)
]
)
}
)
)
decoder
OptimizedDecoder.string
--decrypt : String -> DataSource.DataSource (Dict String Json.Decode.Value)
decrypt : OptimizedDecoder.Decoder a -> String -> DataSource a
decrypt decoder input =
--let
-- decoder : OptimizedDecoder.Decoder (Dict String Json.Decode.Value)
-- decoder =
-- OptimizedDecoder.dict OptimizedDecoder.value
--in
decrypt : Secrets.Value (List String) -> OptimizedDecoder.Decoder a -> String -> DataSource a
decrypt secrets decoder input =
DataSource.Http.request
(Secrets.succeed
{ url = "port://decrypt"
, method = "GET"
, headers = []
, body = DataSource.Http.jsonBody (Json.Encode.string input)
}
(secrets
|> Secrets.map
(\secretList ->
{ url = "port://decrypt"
, method = "GET"
, headers = []
, body =
DataSource.Http.jsonBody
(Json.Encode.object
[ ( "input", Json.Encode.string input )
, ( "secrets", Json.Encode.list Json.Encode.string secretList )
]
)
}
)
)
decoder
@ -201,35 +192,11 @@ data routeParams =
-- ("hello " ++ requestData.username ++ "!")
-- |> DataSource.succeed
-- ),
--, withSession
-- { name = "__session"
-- , secrets =
-- Secrets.succeed
-- (\sessionSecret -> [ sessionSecret ])
-- |> Secrets.with "SESSION_SECRET"
-- , sameSite = "lax" -- TODO custom type
-- , codec =
-- -- TODO use custom codec API, allowing you to retrieve fields, decode them, and set fields with flash
-- Codec.object identity
-- |> Codec.field "userId" identity Codec.string
-- |> Codec.buildObject
-- }
-- (\userIdResult ->
-- case userIdResult of
-- Err error ->
-- Debug.todo ""
--
-- Ok userId ->
-- Request.succeed
-- (DataSource.succeed
-- ( userId, Server.Response.temporaryRedirect "/login" )
-- )
-- )
withSession
{ name = "mysession"
, secrets =
Secrets.succeed
(\sessionSecret -> [ sessionSecret ])
(\sessionSecret -> [ "secret4", "secret3", "secret2", sessionSecret ])
|> Secrets.with "SESSION_SECRET"
, sameSite = "lax" -- TODO custom type
}

View File

@ -2,6 +2,7 @@ const path = require("path");
const undici = require("undici");
const objectHash = require("object-hash");
const kleur = require("kleur");
const cookie = require("cookie-signature");
/**
* To cache HTTP requests on disk with quick lookup and insertion, we store the hashed request.
@ -56,26 +57,13 @@ function lookupOrPerform(mode, rawRequest, hasFsAccess) {
} catch (e) {}
if (request.url === "port://encrypt") {
const cookie = require("cookie-signature");
console.log(
"@@@signing",
rawRequest.body.args[0],
typeof rawRequest.body.args[0]
);
console.log(
"signed",
cookie.sign(
JSON.stringify(rawRequest.body.args[0], null, 0),
"abcdefg"
)
);
try {
await fs.promises.writeFile(
responsePath,
JSON.stringify(
cookie.sign(
JSON.stringify(rawRequest.body.args[0], null, 0),
"abcdefg"
JSON.stringify(rawRequest.body.args[0].values, null, 0),
rawRequest.body.args[0].secret
)
)
);
@ -91,18 +79,14 @@ function lookupOrPerform(mode, rawRequest, hasFsAccess) {
});
}
} else if (request.url === "port://decrypt") {
const cookie = require("cookie-signature");
console.log("@@@[0]", rawRequest.body.args[0]);
try {
console.log(
"unsigned",
cookie.unsign(rawRequest.body.args[0], "abcdefg")
);
// TODO if unsign returns `false`, need to have an `Err` in Elm because decryption failed
await fs.promises.writeFile(
responsePath,
cookie.unsign(rawRequest.body.args[0], "abcdefg")
const signed = tryDecodeCookie(
rawRequest.body.args[0].input,
rawRequest.body.args[0].secrets
);
await fs.promises.writeFile(responsePath, signed);
resolve(responsePath);
} catch (e) {
reject({
@ -181,6 +165,19 @@ function lookupOrPerform(mode, rawRequest, hasFsAccess) {
});
}
function tryDecodeCookie(input, secrets) {
if (secrets.length > 0) {
const signed = cookie.unsign(input, secrets[0]);
if (signed) {
return signed;
} else {
return tryDecodeCookie(input, secrets.slice(1));
}
} else {
return null;
}
}
/**
* @param {{url: string; headers: {[x: string]: string}; method: string; body: Body } } elmRequest
*/