packages feed

github-app-token-0.0.0.1: src/GitHub/App/Token/JWT.hs

module GitHub.App.Token.JWT
  ( signJWT
  , ExpirationTime (..)
  , Issuer (..)

    -- * Private RSA Key data
  , PrivateKey (..)

    -- * Errors
  , InvalidPrivateKey (..)
  , InvalidDate (..)
  , InvalidIssuer (..)
  ) where

import GitHub.App.Token.Prelude

import Data.Text.Encoding (encodeUtf8)
import Data.Time (NominalDiffTime, addUTCTime, getCurrentTime)
import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds)
import Web.JWT qualified as JWT

newtype ExpirationTime = ExpirationTime
  { unwrap :: NominalDiffTime
  }

newtype Issuer = Issuer
  { unwrap :: Text
  }
  deriving stock (Show)

newtype PrivateKey = PrivateKey
  { unwrap :: ByteString
  }
  deriving stock (Show)

newtype InvalidPrivateKey = InvalidPrivateKey PrivateKey
  deriving stock (Show)
  deriving anyclass (Exception)

data InvalidDate = InvalidDate
  { field :: String
  , date :: UTCTime
  }
  deriving stock (Show)
  deriving anyclass (Exception)

newtype InvalidIssuer = InvalidIssuer Issuer
  deriving stock (Show)
  deriving anyclass (Exception)

signJWT
  :: MonadIO m
  => ExpirationTime
  -> Issuer
  -> PrivateKey
  -> m ByteString
signJWT expirationTime issuer privateKey = liftIO $ do
  now <- getCurrentTime
  let expiration = addUTCTime expirationTime.unwrap now

  signer <-
    maybe
      (throwIO $ InvalidPrivateKey privateKey)
      (pure . JWT.EncodeRSAPrivateKey)
      $ JWT.readRsaSecret privateKey.unwrap

  iat <-
    maybe (throwIO $ InvalidDate "iat" now) pure
      $ numericDate now

  exp <-
    maybe (throwIO $ InvalidDate "exp" expiration) pure
      $ numericDate expiration

  iss <-
    maybe (throwIO $ InvalidIssuer issuer) pure
      $ JWT.stringOrURI issuer.unwrap

  pure
    $ encodeUtf8
    $ JWT.encodeSigned
      signer
      mempty {JWT.alg = Just JWT.RS256}
      mempty
        { JWT.iat = Just iat
        , JWT.exp = Just exp
        , JWT.iss = Just iss
        }

numericDate :: UTCTime -> Maybe JWT.NumericDate
numericDate = JWT.numericDate . utcTimeToPOSIXSeconds