Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/5-internal/pr-3112
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Rust library `rusty-jwt-tools` upgraded to latest version
26 changes: 13 additions & 13 deletions libs/jwt-tools/src/Data/Jwt/Tools.hs
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,13 @@ import Foreign.Storable (peek)
import Imports
import Network.HTTP.Types (StdMethod (..))

data JwtResponse
data HsResult

type ProofCStr = CString

type UserIdCStr = CString

type ClientIdWord16 = Word16
type ClientIdWord64 = Word64

type DomainCStr = CString

Expand All @@ -75,7 +75,7 @@ foreign import ccall unsafe "generate_dpop_access_token"
generate_dpop_access_token ::
ProofCStr ->
UserIdCStr ->
ClientIdWord16 ->
ClientIdWord64 ->
DomainCStr ->
NonceCStr ->
UrlCStr ->
Expand All @@ -84,18 +84,18 @@ foreign import ccall unsafe "generate_dpop_access_token"
ExpiryEpochWord64 ->
EpochWord64 ->
BackendBundleCStr ->
IO (Ptr JwtResponse)
IO (Ptr HsResult)

foreign import ccall unsafe "free_dpop_access_token" free_dpop_access_token :: Ptr JwtResponse -> IO ()
foreign import ccall unsafe "free_dpop_access_token" free_dpop_access_token :: Ptr HsResult -> IO ()

foreign import ccall unsafe "get_error" get_error :: Ptr JwtResponse -> Ptr CUChar
foreign import ccall unsafe "get_error" get_error :: Ptr HsResult -> Ptr CUChar

foreign import ccall unsafe "get_token" get_token :: Ptr JwtResponse -> CString
foreign import ccall unsafe "get_token" get_token :: Ptr HsResult -> CString

generateDpopAccessTokenFfi ::
ProofCStr ->
UserIdCStr ->
ClientIdWord16 ->
ClientIdWord64 ->
DomainCStr ->
NonceCStr ->
UrlCStr ->
Expand All @@ -104,21 +104,21 @@ generateDpopAccessTokenFfi ::
ExpiryEpochWord64 ->
EpochWord64 ->
BackendBundleCStr ->
IO (Maybe (Ptr JwtResponse))
IO (Maybe (Ptr HsResult))
generateDpopAccessTokenFfi dpopProof user client domain nonce uri method maxSkewSecs expiration now backendKeys = do
ptr <- generate_dpop_access_token dpopProof user client domain nonce uri method maxSkewSecs expiration now backendKeys
if ptr /= nullPtr
then pure $ Just ptr
else pure Nothing

getErrorFfi :: Ptr JwtResponse -> IO (Maybe Word8)
getErrorFfi :: Ptr HsResult -> IO (Maybe Word8)
getErrorFfi ptr = do
let errorPtr = get_error ptr
if errorPtr /= nullPtr
then Just . fromIntegral <$> peek errorPtr
else pure Nothing

getTokenFfi :: Ptr JwtResponse -> IO (Maybe String)
getTokenFfi :: Ptr HsResult -> IO (Maybe String)
getTokenFfi ptr = do
let tokenPtr = get_token ptr
if tokenPtr /= nullPtr
Expand Down Expand Up @@ -190,7 +190,7 @@ generateDpopToken dpopProof uid cid domain nonce uri method maxSkewSecs maxExpir
PATCH -> "PATCH"

toResult :: Maybe Word8 -> Maybe String -> Either DPoPTokenGenerationError ByteString
-- the only valid case is when the error=0 (meaning no error) and the token is not null
-- the only valid cases are when the error=0 (meaning no error) or nothing and the token is not null
toResult (Just 0) (Just token) = Right $ cs token
toResult Nothing (Just token) = Right $ cs token
-- errors
Expand All @@ -212,7 +212,7 @@ newtype UserId = UserId {_unUserId :: ByteString}
deriving (Eq, Show)
deriving newtype (ToByteString)

newtype ClientId = ClientId {_unClientId :: Word16}
newtype ClientId = ClientId {_unClientId :: Word64}
deriving (Eq, Show)
deriving newtype (ToByteString)

Expand Down
81 changes: 54 additions & 27 deletions libs/jwt-tools/test/Spec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@ import Test.Hspec

main :: IO ()
main = hspec $ do
describe "generateDpopToken FFI" $ do
it "should return a value" $ do
actual <- callFFIWithConstValues
let expected = Right $ cs token
actual `shouldBe` expected
describe "generateDpopToken FFI when passing valid inputs" $ do
it "should return an access token" $ do
actual <- callFFIWithValidValues
isRight actual `shouldBe` True
describe "generateDpopToken FFI when passing nonsense values" $ do
it "should return an error" $ do
actual <- callFFIWithNonsenseValues
isRight actual `shouldBe` False
describe "toResult" $ do
it "should convert to correct error" $ do
toResult Nothing (Just token) `shouldBe` Right (cs token)
Expand Down Expand Up @@ -68,28 +71,52 @@ main = hspec $ do
toResult (Just 18) Nothing `shouldBe` Left ExpError
toResult (Just 18) (Just token) `shouldBe` Left ExpError
toResult Nothing Nothing `shouldBe` Left UnknownError
where
token :: String
token = "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"

token :: String
token = "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
callFFIWithNonsenseValues :: IO (Either DPoPTokenGenerationError ByteString)
callFFIWithNonsenseValues =
runExceptT $ generateDpopToken proof uid cid domain nonce uri method maxSkewSecs expires now pem
where
proof = Proof "xxxx.yyyy.zzzz"
uid = UserId "8a6e8a6e-8a6e-8a6e-8a6e-8a6e8a6e8a6e"
cid = ClientId 8899
domain = Domain "example.com"
nonce = Nonce "123"
uri = Uri "/foo"
method = POST
maxSkewSecs = MaxSkewSecs 1
now = NowEpoch 5435234232
expires = ExpiryEpoch $ 5435234232 + 360
pem =
PemBundle $
"-----BEGIN PRIVATE KEY-----\n\
\MC4CAQAwBQYDK2VwBCIEIFANnxZLNE4p+GDzWzR3wm/v8x/0bxZYkCyke1aTRucX\n\
\-----END PRIVATE KEY-----\n\
\-----BEGIN PUBLIC KEY-----\n\
\MCowBQYDK2VwAyEACPvhIdimF20tOPjbb+fXJrwS2RKDp7686T90AZ0+Th8=\n\
\-----END PUBLIC KEY-----\n"

callFFIWithConstValues :: IO (Either DPoPTokenGenerationError ByteString)
callFFIWithConstValues = do
let proof = Proof "xxxx.yyyy.zzzz"
let uid = UserId "8a6e8a6e-8a6e-8a6e-8a6e-8a6e8a6e8a6e"
let cid = ClientId 8899
let domain = Domain "example.com"
let nonce = Nonce "123"
let uri = Uri "/foo"
let method = POST
let maxSkewSecs = MaxSkewSecs 1
let now = NowEpoch 5435234232
let expires = ExpiryEpoch $ 5435234232 + 360
let pem =
PemBundle $
"-----BEGIN PRIVATE KEY-----\n\
\MC4CAQAwBQYDK2VwBCIEIFANnxZLNE4p+GDzWzR3wm/v8x/0bxZYkCyke1aTRucX\n\
\-----END PRIVATE KEY-----\n\
\-----BEGIN PUBLIC KEY-----\n\
\MCowBQYDK2VwAyEACPvhIdimF20tOPjbb+fXJrwS2RKDp7686T90AZ0+Th8=\n\
\-----END PUBLIC KEY-----\n"
callFFIWithValidValues :: IO (Either DPoPTokenGenerationError ByteString)
callFFIWithValidValues =
runExceptT $ generateDpopToken proof uid cid domain nonce uri method maxSkewSecs expires now pem
where
proof = Proof "eyJhbGciOiJFZERTQSIsInR5cCI6ImRwb3Arand0IiwiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4IjoiZzQwakI3V3pmb2ZCdkxCNVlybmlZM2ZPZU1WVGtfNlpfVnNZM0tBbnpOUSJ9fQ.eyJpYXQiOjE2Nzc2NzAwODEsImV4cCI6MTY3Nzc1NjQ4MSwibmJmIjoxNjc3NjcwMDgxLCJzdWIiOiJpbXBwOndpcmVhcHA9WldKa01qY3labUk0TW1aa05ETXlZamczTm1NM1lXSmtZVFUwWkdSaU56VS8xODllNDhjNmNhODZiNWQ0QGV4YW1wbGUub3JnIiwianRpIjoiZDE5ZWExYmItNWI0Ny00ZGJiLWE1MTktNjU0ZWRmMjU0MTQ0Iiwibm9uY2UiOiJZMkZVTjJaTlExUnZSV0l6Ympsa2RGRjFjWGhHZDJKbWFXUlRiamhXZVdRIiwiaHRtIjoiUE9TVCIsImh0dSI6Imh0dHA6Ly9sb2NhbGhvc3Q6NjQwNTQvIiwiY2hhbCI6IkJpMkpkUGk1eWVTTVdhZjA5TnJEZTVUQXFjZ0FnQmE3In0._PrwHUTS7EoAflXyNDlPNqGMbjKu-JuSXwkNPyryBQdg2gDIb20amsH05Ocih78Josz9h7lAB6FvAWsXKQB1Dw"
uid = UserId "ebd272fb-82fd-432b-876c-7abda54ddb75"
cid = ClientId 1773935321869104596
domain = Domain "example.org"
nonce = Nonce "Y2FUN2ZNQ1RvRWIzbjlkdFF1cXhGd2JmaWRTbjhWeWQ"
uri = Uri "http://localhost:64054/"
method = POST
maxSkewSecs = MaxSkewSecs 2
now = NowEpoch 5435234232
expires = ExpiryEpoch $ 2082008461
pem =
PemBundle $
"-----BEGIN PRIVATE KEY-----\n\
\MC4CAQAwBQYDK2VwBCIEIKW3jzXCsRVgnclmiTu53Pu1/r6AUmnKDoghOOVMjozQ\n\
\-----END PRIVATE KEY-----\n\
\-----BEGIN PUBLIC KEY-----\n\
\MCowBQYDK2VwAyEA7t9veqi02mPhllm44JXWga8m/l4JxUeQm3qPyMlerxY=\n\
\-----END PUBLIC KEY-----\n"
Loading